From 7aeb776ce03a1e6d4d49c01da80f0f898bc61e50 Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Sun, 22 Feb 2026 00:42:33 +0100
Subject: [PATCH 1/6] add run dir for myself
---
.gitignore | 3 +++
.run/Server.run.xml | 9 +++++++++
2 files changed, 12 insertions(+)
create mode 100644 .run/Server.run.xml
diff --git a/.gitignore b/.gitignore
index e7d8069..982299c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,9 @@
/impl/*/build
/impl/*/.gradle
+# run folder
+/run/
+
# other random folders
/htmlReport
/.kotlin/errors
diff --git a/.run/Server.run.xml b/.run/Server.run.xml
new file mode 100644
index 0000000..9a8887f
--- /dev/null
+++ b/.run/Server.run.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From ae8167faecaa0ad15a6609e35699364edd4b56de Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Sun, 22 Feb 2026 01:07:14 +0100
Subject: [PATCH 2/6] safer start
---
src/main/kotlin/io/delilaheve/CustomAnvil.kt | 102 +++++++++++++++----
1 file changed, 82 insertions(+), 20 deletions(-)
diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
index c747189..ca25147 100644
--- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt
+++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
@@ -81,12 +81,91 @@ open class CustomAnvil : JavaPlugin() {
}
+ // stop plugin if we do not force a dirty start (true by default)
+ // Return true if start was stopped
+ private fun tryDirtyStart(): Boolean {
+ if(!ConfigHolder.DEFAULT_CONFIG.config.getBoolean("dirty_start", false)) {
+ Bukkit.getPluginManager().disablePlugin(this)
+ return true
+ }
+ return false
+ }
+
+ // stop plugin if we force a safe start (false by default)
+ // Return true if start was stopped
+ private fun trySafeStart(): Boolean {
+ if(ConfigHolder.DEFAULT_CONFIG.config.getBoolean("safe_start", false)) {
+ Bukkit.getPluginManager().disablePlugin(this)
+ return true
+ }
+ return false
+ }
+
/**
* Setup plugin for use
*/
override fun onEnable() {
instance = this
+ try {
+ legacyCheck()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error trying to check for legacy system" , e)
+ if(trySafeStart()) return
+ }
+ // Add commands
+ try {
+ prepareCommand()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error trying to register commands" , e)
+ if(trySafeStart()) return
+ }
+
+ // Load default configuration
+ try {
+ if(!ConfigHolder.loadDefaultConfig())
+ throw RuntimeException("Error loading configuration file")
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error occurred loading default configuration", e)
+ if(tryDirtyStart()) return
+ }
+
+ // Load dependency
+ try {
+ DependencyManager.loadDependency()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error loading dependency compatibility", e)
+ if(tryDirtyStart()) return
+ }
+
+ // Register listeners
+ try {
+ registerListeners()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error registering listeners", e)
+ if(tryDirtyStart()) return
+ }
+
+ // Load metrics
+ try {
+ Metrics(this, bstatsPluginId)
+ } catch (_: Exception) {}
+
+ // Load other thing later.
+ // It is so other dependent plugins can implement there event listener before we fire them.
+ DependencyManager.scheduler.scheduleGlobally(this) { loadEnchantmentSystemDirty() }
+ }
+
+ private fun loadEnchantmentSystemDirty() {
+ try {
+ loadEnchantmentSystem()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error initializing enchantment ssytem", e)
+ tryDirtyStart()
+ }
+ }
+
+ private fun legacyCheck() {
// Disable old plugin name if exist
val potentialPlugin = Bukkit.getPluginManager().getPlugin("UnsafeEnchantsPlus")
if (potentialPlugin != null) {
@@ -99,34 +178,17 @@ open class CustomAnvil : JavaPlugin() {
logger.warning("It seems you are using spigot")
logger.warning("Please take notice that spigot is less supported than paper and derivatives")
}
+ }
- // Add commands
- prepareCommand()
-
- // Load chat listener
+ private fun registerListeners() {
+ // Register chat listener
chatListener = ChatEventListener()
server.pluginManager.registerEvents(chatListener, this)
- // Load default configuration
- if (!ConfigHolder.loadDefaultConfig()) {
- logger.log(Level.SEVERE,"could not load default config.")
- return
- }
-
- // Load dependency
- DependencyManager.loadDependency()
-
// Register anvil events
server.pluginManager.registerEvents(PrepareAnvilListener(), this)
server.pluginManager.registerEvents(AnvilResultListener(), this)
server.pluginManager.registerEvents(AnvilCloseListener(DependencyManager.packetManager), this)
-
- // Load metrics
- Metrics(this, bstatsPluginId)
-
- // Load other thing later.
- // It is so other dependent plugins can implement there event listener before we fire them.
- DependencyManager.scheduler.scheduleGlobally(this, {loadEnchantmentSystem()})
}
private fun loadEnchantmentSystem(){
From c57de03442c69c558d528211a9890619a67d7ea3 Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Sun, 22 Feb 2026 04:02:20 +0100
Subject: [PATCH 3/6] add credits move compatibility list & remove spigot link
spigot is not recomended anymore as do not have auto upload
---
COMPATIBILITY.MD | 52 ++++++++++++++++++++++++++++++++++++++++++++++++
CREDITS.MD | 35 ++++++++++++++++++++++++++++++++
README.md | 39 ++++++------------------------------
3 files changed, 93 insertions(+), 33 deletions(-)
create mode 100644 COMPATIBILITY.MD
create mode 100644 CREDITS.MD
diff --git a/COMPATIBILITY.MD b/COMPATIBILITY.MD
new file mode 100644
index 0000000..2be6f7a
--- /dev/null
+++ b/COMPATIBILITY.MD
@@ -0,0 +1,52 @@
+### Bedrock issue
+For server using geyser, bedrock player cannot use custom "recipes" in the anvil.
+This is cannot be fixed on geyser or my side.
+
+### Plugin Compatibility
+Here is various plugins that had issues with CustomAnvil
+where efforts was made for compatibility and should be working right:
+
+some of them are cool I recommend checking them out !
+
+## Supported By CustomAnvil
+These plugins have compatibility handled by custom anvil. seek help on custom anvil and do not bother these developers
+
+#### Enchantment plugins
+- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/):
+ Use ExcellentEnchants item type
+
+- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/):
+ Need to use /anvilconfigreload or a server restart to add newly added enchantment.
+ Use EcoEnchant restriction system but new restriction can be added in custom anvil
+
+- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/):
+ Support by Custom Anvil but still experimental. Automatic configuration. Plugin is not actively developed anymore
+
+#### Anvil Mechanics
+- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
+ Partially use Custom Anvil maximum XP settings (>= 6.1.5)
+
+- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/)
+ For bag upgrade and skin via anvil. (version >= 1.31.0)
+
+- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi)
+ For its anvil inventory usage
+
+- [ToolsStats](https://modrinth.com/project/oBZj9E15)
+ For token application using anvil
+
+### Known Partially Incompatible
+- [UberEnchant](https://modrinth.com/plugin/uberenchant)
+ Anvil handling as they are doing something similar to CustomAnvil.
+It is by no mean there faults and I recomend checking them out
+
+- [SuperEnchant](https://modrinth.com/plugin/superenchants)
+ Reported potential incompatibility
+
+- [AdvencedEnchantments](https://ae.advancedplugins.net/)
+ Paid plugin I do not own as I did not get commissioned for support.
+ may be able to use api but cannot test on my side
+
+If you like Custom Anvil to support a specific plugin (custom enchant or anvil mechanic).
+You can ask, but please note implementing compatibility will be considered
+as low priority as I work for the plugin as an hobby on my free time for free.
diff --git a/CREDITS.MD b/CREDITS.MD
new file mode 100644
index 0000000..5311480
--- /dev/null
+++ b/CREDITS.MD
@@ -0,0 +1,35 @@
+**Custom Anvil** is based on [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants) by DelilahEve.
+
+Thanks for all the contributors of bukkit, spigot, the paper team and the adventure API developers
+thanks JetBrain for making IntelliJ
+
+### Dependencies
+Here dependencies are used by custom anvil
+- [IF](https://github.com/stefvanschie/IF) an inventory framework by stefvanschie
+- [Mockbukkit](https://github.com/MockBukkit/MockBukkit) for unit testing
+- [CentralPortalPlus](https://github.com/lalakii/central-portal-plus) by lalakii
+- [test-summary-action](https://github.com/jeantessier/test-summary-action) by jeantessier
+- [modrinth-publish](https://github.com/cloudnode-pro/modrinth-publish) by Zefir
+- [discord-webhook](https://github.com/tsickert/discord-webhook) by tsickert
+
+### Compatibility
+Here is to credits all the author of plugins
+It partially repeat the the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
+- Big Thanks for H7KZ for [Disenchantment](https://github.com/H7KZ/Disenchantment)
+- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/) by Athlaeos
+- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/) by Auxilor
+- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/) by NightExpress
+- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/) by hyperdefined
+- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi) by ArtillexStudios
+- [ToolsStats](https://modrinth.com/project/oBZj9E15) by Valorless
+
+### Special Thanks
+
+Thanks for Microsoft leading me into using a better operating system
+Thanks for all the users trying my plugin for these niche use cases
+and for reporting issues and giving ideas !
+
+Thanks coltonj96 for [UberEnchant](https://modrinth.com/plugin/uberenchant).
+we may be incompatible with the anvil, but I do think it is a good alternative !
+I wish one day to work on cross compatibiltiy
+
diff --git a/README.md b/README.md
index bf119e9..e1d7ec6 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,10 @@
It is expected to work on 1.18 to 1.21.7 minecraft servers running spigot or paper.
(the plugin support of 1.16.5 to 1.17.1 is experimental and may encounter issues)
-**Custom Anvil** was previously named **Unsafe Enchants+**.
-It was renamed because it now affects every anvil aspect and not only unsafe enchants\
-**Custom Anvil** is based on [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants) by DelilahEve.
-
### Download Locations:
the plugin can be downloaded on
-[Spigot](https://www.spigotmc.org/resources/custom-anvil.114884),
- [modrinth](https://modrinth.com/plugin/customanvil),
+ [Modrinth](https://modrinth.com/plugin/customanvil),
[Hangar](https://hangar.papermc.io/alexcrea/CustomAnvil)
or here [on GitHub](https://github.com/alexcrea/CustomAnvil/releases/latest)
@@ -57,32 +52,7 @@ anvilconfigreload or carl: Reload every config of this plugin
customanvilconfig or configanvil: open a menu for administrator to edit plugin's config in game
```
### Supported Plugins
-Custom Anvil can be compatible with some custom enchantments and anvil mechanics plugins.
-
-Here is a list of supported custom enchantment plugins with support status:
-- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/):
-Support by Custom Anvil but still experimental. Automatic configuration.
-
-- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/):
-Support by Custom Anvil but still experimental. Need to use /anvilconfigreload or a server restart to add newly added enchantment.
-Use EcoEnchant restriction system by default.
-
-- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/):
-Support by Custom Anvil but still experimental. Use ExcellentEnchants item type.
-
-- [Superenchants](https://modrinth.com/plugin/superenchants)
-support by Superenchants. Use CustomAnvil to combine enchantment in anvil in survival.
-
-Here is a list of supported anvil mechanic plugins with support status:
-- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
-support by Custom Anvil but still experimental. Mostly use Custom Anvil basic XP settings. (version >= 6.1.5)
-
-- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/)
-support by Custom Anvil. Not really enchantment related but CustomAnvil should not impact bag upgrade and skin via anvil. (version >= 1.31.0)
-
-If you like Custom Anvil to support a specific plugin (custom enchant or anvil mechanic).
-You can ask, but please note implementing compatibility will be considered
-as low priority as I work for the plugin on my free time for free.
+See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
### Overriding Too Expensive
@@ -108,9 +78,12 @@ see [Here](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs)
---
Custom anvil [use bstat](https://bstats.org/plugin/bukkit/Unsafe%20Enchants%20Plus/20923) for metric. You can [disable it](https://bstats.org/getting-started) if you like.
+### Credits and Thanks
+Credits and thanks can be seen [here](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/CREDITS.md)
+
### Planned:
- Better Folia support (make gui work. fix some dirty handled parts)
-- Get restriction on unknown enchantments
+- Get restriction on unknown enchantments (planned for V2)
- More features for custom anvil craft
### Known issue:
From 9cf06fbb93299e4609e83d614818e016df1e69a5 Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Sun, 22 Feb 2026 04:15:30 +0100
Subject: [PATCH 4/6] add bstat in credit
---
CREDITS.MD | 1 +
1 file changed, 1 insertion(+)
diff --git a/CREDITS.MD b/CREDITS.MD
index 5311480..8ac2464 100644
--- a/CREDITS.MD
+++ b/CREDITS.MD
@@ -11,6 +11,7 @@ Here dependencies are used by custom anvil
- [test-summary-action](https://github.com/jeantessier/test-summary-action) by jeantessier
- [modrinth-publish](https://github.com/cloudnode-pro/modrinth-publish) by Zefir
- [discord-webhook](https://github.com/tsickert/discord-webhook) by tsickert
+- [bstats](https://bstats.org/) for keeping me motivated
### Compatibility
Here is to credits all the author of plugins
From d801d8524271f466b06a1f3f49d844f4b51a9376 Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Sun, 22 Feb 2026 04:19:07 +0100
Subject: [PATCH 5/6] better formating
---
CREDITS.MD | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/CREDITS.MD b/CREDITS.MD
index 8ac2464..e4f2533 100644
--- a/CREDITS.MD
+++ b/CREDITS.MD
@@ -1,7 +1,8 @@
-**Custom Anvil** is based on [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants) by DelilahEve.
+Thanks **DelilahEve** for making [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants). \
+CustomAnvil was initially a fork of Unsafe Enchants where I wanted to add more and more and here we are now !
-Thanks for all the contributors of bukkit, spigot, the paper team and the adventure API developers
-thanks JetBrain for making IntelliJ
+Thanks for all the contributors of bukkit, spigot, the paper team and the adventure API developers \
+Thanks JetBrain for making IntelliJ
### Dependencies
Here dependencies are used by custom anvil
@@ -11,7 +12,7 @@ Here dependencies are used by custom anvil
- [test-summary-action](https://github.com/jeantessier/test-summary-action) by jeantessier
- [modrinth-publish](https://github.com/cloudnode-pro/modrinth-publish) by Zefir
- [discord-webhook](https://github.com/tsickert/discord-webhook) by tsickert
-- [bstats](https://bstats.org/) for keeping me motivated
+- Thanks [bstats](https://bstats.org/) for keeping me motivated
### Compatibility
Here is to credits all the author of plugins
@@ -26,11 +27,11 @@ It partially repeat the the [Compatibility list](https://github.com/alexcrea/Cus
### Special Thanks
-Thanks for Microsoft leading me into using a better operating system
+Thanks for Microsoft leading me into using a better operating system \
Thanks for all the users trying my plugin for these niche use cases
and for reporting issues and giving ideas !
Thanks coltonj96 for [UberEnchant](https://modrinth.com/plugin/uberenchant).
-we may be incompatible with the anvil, but I do think it is a good alternative !
+we may be incompatible with the anvil, but I do think it is a good alternative ! \
I wish one day to work on cross compatibiltiy
From 49abca2ccfee01eabcb425ada89ad91d539c0c92 Mon Sep 17 00:00:00 2001
From: alexd <42614139+alexcrea@users.noreply.github.com>
Date: Fri, 27 Feb 2026 19:44:30 +0100
Subject: [PATCH 6/6] update checker
---
CREDITS.MD | 1 +
.../cuanvil/update/ModrinthUpdateChecker.java | 214 ++++++++++++++++++
src/main/kotlin/io/delilaheve/CustomAnvil.kt | 21 +-
3 files changed, 234 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java
diff --git a/CREDITS.MD b/CREDITS.MD
index e4f2533..b4a3ce8 100644
--- a/CREDITS.MD
+++ b/CREDITS.MD
@@ -13,6 +13,7 @@ Here dependencies are used by custom anvil
- [modrinth-publish](https://github.com/cloudnode-pro/modrinth-publish) by Zefir
- [discord-webhook](https://github.com/tsickert/discord-webhook) by tsickert
- Thanks [bstats](https://bstats.org/) for keeping me motivated
+- [ModrinthUpdateChecker](https://github.com/Clickism/ModrinthUpdateChecker) by Clickism and thanks to the modrinth team
### Compatibility
Here is to credits all the author of plugins
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java b/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java
new file mode 100644
index 0000000..489c636
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java
@@ -0,0 +1,214 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2025 Clickism
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package xyz.alexcrea.cuanvil.update;
+
+import com.google.gson.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Utility class to check for newer versions of a project hosted on Modrinth.
+ */
+public class ModrinthUpdateChecker {
+
+ private static final String API_URL = "https://api.modrinth.com/v2/project/{id}/version";
+
+ private final String projectId;
+ private final String loader;
+ @Nullable
+ private final String minecraftVersion;
+
+ @Nullable
+ private Boolean featured = null;
+
+ @Nullable
+ public Consumer onError = null;
+ @Nullable
+ public Function getRawVersion = ModrinthUpdateChecker::getRawVersion;
+
+ /**
+ * Create a new update checker for the given project.
+ * This will check the latest version for the given loader and any minecraft version.
+ *
+ * @param projectId the project ID
+ * @param loader the loader
+ */
+ public ModrinthUpdateChecker(String projectId, String loader) {
+ this(projectId, loader, null);
+ }
+
+ /**
+ * Create a new update checker for the given project.
+ * This will check the latest version for the given loader and minecraft version.
+ *
+ * @param projectId the project ID
+ * @param loader the loader
+ * @param minecraftVersion the minecraft version, or null for any version
+ */
+ public ModrinthUpdateChecker(String projectId, String loader, @Nullable String minecraftVersion) {
+ this.projectId = projectId;
+ this.loader = loader;
+ this.minecraftVersion = minecraftVersion;
+ }
+
+ /**
+ * Check the latest version of the project for the given loader and minecraft version
+ * and call the consumer with it.
+ *
+ * @param consumer the consumer
+ */
+ public void checkVersion(Consumer consumer) {
+ try {
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(prepareURI())
+ .GET()
+ .build();
+
+ client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+ .thenAcceptAsync(response -> {
+ if (response.statusCode() != 200) {
+ if(onError != null)
+ onError.accept(new RuntimeException("wrong response status code: " + response.statusCode()));
+ return;
+ }
+ JsonArray versionsArray = JsonParser.parseString(response.body()).getAsJsonArray();
+ String latestVersion = getLatestVersion(versionsArray);
+ if (latestVersion == null) {
+ if(onError != null)
+ onError.accept(new RuntimeException("latest version is null"));
+ return;
+ }
+ consumer.accept(latestVersion);
+ });
+ } catch (Exception e) {
+ if(onError != null) onError.accept(e);
+ }
+ }
+
+ /**
+ * Get the latest compatible version from the versions array.
+ *
+ * @param versions the versions array
+ * @return the latest compatible version
+ */
+ @Nullable
+ protected String getLatestVersion(JsonArray versions) {
+ return versions.asList().stream().findFirst()
+ .map(JsonElement::getAsJsonObject)
+ .map(version -> version.get("version_number").getAsString())
+ .map(getRawVersion != null ? getRawVersion : (v -> v))
+ .orElse(null);
+ }
+
+ /**
+ * Gets the raw version from a version string.
+ * i.E: "fabric-1.2+1.17.1" -> "1.2"
+ *
+ * @param version the version string
+ * @return the raw version string
+ */
+ public static String getRawVersion(String version) {
+ if (version.isEmpty()) return version;
+ version = version.replaceAll("^\\D+", "");
+ String[] split = version.split("\\+");
+ return split[0];
+ }
+
+ /**
+ * Prepare this request uri based on current parameters.
+ * @return the request uri
+ */
+ private URI prepareURI() {
+ var url = new StringBuilder(API_URL.replace("{id}", projectId));
+
+ var parameters = prepareParameters();
+ String[] paramArray = new String[parameters.size()];
+ int i = 0;
+ for (Map.Entry entry : parameters.entrySet()) {
+ paramArray[i++] = entry.getKey() + '=' + entry.getValue();
+ }
+ url.append('?').append(String.join("&", paramArray));
+
+ return URI.create(url.toString());
+ }
+
+ /**
+ * Get the parameters for the version request.
+ *
+ * @return a map of key-value map of the request parameters
+ */
+ private Map prepareParameters(){
+ var parameters = new HashMap();
+
+ parameters.put("loaders", List.of(loader).toString());
+ if(minecraftVersion != null) parameters.put("game_versions", List.of(minecraftVersion).toString());
+ if(featured != null) parameters.put("featured", featured.toString());
+
+ parameters.put("include_changelog", "false");
+ return parameters;
+ }
+
+ /**
+ * Only get featured or non-featured versions.
+ * Null represent no filter.
+ * @param featured should be restricted to featured version ? default null if not called
+ * @return this
+ */
+ public ModrinthUpdateChecker setFeatured(@Nullable Boolean featured) {
+ this.featured = featured;
+ return this;
+ }
+
+ /**
+ * Function called on error calling the api.
+ * @param onError What should happen on error
+ * @return this
+ */
+ public ModrinthUpdateChecker setOnError(@Nullable Consumer onError) {
+ this.onError = onError;
+ return this;
+ }
+
+ /**
+ * Set the function to get raw version from the modrinth version.
+ * If null provided raw version will act as in the identity function.
+ * @param getRawVersion The function transforming modrinth version to raw version
+ * @return this
+ */
+ public ModrinthUpdateChecker setGetRawVersion(@Nullable Function getRawVersion) {
+ this.getRawVersion = getRawVersion;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
index ca25147..b48f4a6 100644
--- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt
+++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
@@ -18,6 +18,7 @@ import xyz.alexcrea.cuanvil.listener.AnvilCloseListener
import xyz.alexcrea.cuanvil.listener.AnvilResultListener
import xyz.alexcrea.cuanvil.listener.ChatEventListener
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
+import xyz.alexcrea.cuanvil.update.ModrinthUpdateChecker
import xyz.alexcrea.cuanvil.update.PluginSetDefault
import xyz.alexcrea.cuanvil.update.UpdateHandler
import xyz.alexcrea.cuanvil.util.Metrics
@@ -31,8 +32,9 @@ import java.util.logging.Level
open class CustomAnvil : JavaPlugin() {
companion object {
- // bstats plugin id
+ // pluginIDS
private const val bstatsPluginId = 20923
+ private const val modrinthPluginID = "S75Ueiq9"
// Permission string required to use the plugin's features
const val affectedByPluginPermission = "ca.affected"
@@ -174,10 +176,25 @@ open class CustomAnvil : JavaPlugin() {
logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus")
}
- if(!PlatformUtil.isPaper) {
+ val isPaper = PlatformUtil.isPaper
+ if(!isPaper) {
logger.warning("It seems you are using spigot")
logger.warning("Please take notice that spigot is less supported than paper and derivatives")
}
+
+ val loader = if(isPaper) "paper" else "spigot"
+
+ val version = description.version
+ val featured = if(version.contains("dev")) null else true
+
+ ModrinthUpdateChecker(modrinthPluginID, loader, null)
+ .setFeatured(featured)
+ .setOnError { logger.log(Level.WARNING, "error trying to fetch latest update", it) }
+ .checkVersion { latestver: String? ->
+ if(latestver == null || version.contains(latestver)) return@checkVersion
+
+ logger.warning("An update may be available: $latestver")
+ }
}
private fun registerListeners() {