");
} else if (event.isRightClick()) {
if (recipe.getSettings().getFlags().isEmpty()) {
return;
}
// Remove the last flag from the set
- ItemFlag lastFlag = new ArrayList<>(recipe.getSettings().getFlags()).get(recipe.getSettings().getFlags().size() - 1);
+ ItemFlag lastFlag = new ArrayList<>(recipe.getSettings().getFlags()).get(
+ recipe.getSettings().getFlags().size() - 1);
recipe.getSettings().getFlags().remove(lastFlag);
hasChanges = true;
}
diff --git a/src/main/java/studio/magemonkey/fusion/gui/recipe/IngredientFingerprint.java b/src/main/java/studio/magemonkey/fusion/gui/recipe/IngredientFingerprint.java
index d8c0aa7..ff9de7e 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/recipe/IngredientFingerprint.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/recipe/IngredientFingerprint.java
@@ -8,24 +8,24 @@
/**
* Immutable fingerprint for an ItemStack that matches CalculatedRecipe.isSimilar(...) logic.
- *
+ *
* We compare:
- * - Material
- * - customModelData (if present)
- * - displayName (if present)
- * - lore lines (if present)
- * - all enchantments (if present)
- * - unbreakable flag
- * - durability (if Damageable)
+ * - Material
+ * - customModelData (if present)
+ * - displayName (if present)
+ * - lore lines (if present)
+ * - all enchantments (if present)
+ * - unbreakable flag
+ * - durability (if Damageable)
*/
public class IngredientFingerprint {
- private final Material type;
- private final int customModelData;
- private final String displayName;
- private final List lore;
+ private final Material type;
+ private final int customModelData;
+ private final String displayName;
+ private final List lore;
private final Map enchantments;
- private final boolean unbreakable;
- private final int durability;
+ private final boolean unbreakable;
+ private final int durability;
public IngredientFingerprint(Material type,
int customModelData,
@@ -43,17 +43,19 @@ public IngredientFingerprint(Material type,
this.durability = durability;
}
- /** Build an IngredientFingerprint by examining a live ItemStack. */
+ /**
+ * Build an IngredientFingerprint by examining a live ItemStack.
+ */
public static IngredientFingerprint of(ItemStack is) {
- Material mat = is.getType();
+ Material mat = is.getType();
ItemMeta meta = is.getItemMeta();
- int cmd = 0;
- String name = "";
- List loreList = Collections.emptyList();
+ int cmd = 0;
+ String name = "";
+ List loreList = Collections.emptyList();
Map enchantsMap = Collections.emptyMap();
- boolean unbreak = false;
- int dmg = 0;
+ boolean unbreak = false;
+ int dmg = 0;
if (meta != null) {
if (meta.hasCustomModelData()) {
@@ -65,7 +67,7 @@ public static IngredientFingerprint of(ItemStack is) {
if (meta.hasLore()) {
loreList = new ArrayList<>(Objects.requireNonNull(meta.getLore()));
}
- Map raw = meta.getEnchants();
+ Map raw = meta.getEnchants();
if (!raw.isEmpty()) {
enchantsMap = new HashMap<>(raw);
}
diff --git a/src/main/java/studio/magemonkey/fusion/gui/recipe/InventoryFingerprint.java b/src/main/java/studio/magemonkey/fusion/gui/recipe/InventoryFingerprint.java
index d2b0688..1418129 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/recipe/InventoryFingerprint.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/recipe/InventoryFingerprint.java
@@ -13,15 +13,15 @@
/**
* Helper to build a small MD5 fingerprint of an entire PlayerInventory.
* We incorporate:
- * - Material ordinal
- * - amount
- * - customModelData
- * - displayName
- * - lore lines
- * - enchantments
- * - unbreakable
- * - durability if Damageable
- *
+ * - Material ordinal
+ * - amount
+ * - customModelData
+ * - displayName
+ * - lore lines
+ * - enchantments
+ * - unbreakable
+ * - durability if Damageable
+ *
* If MD5 is not available, we fall back to a simple int‐hash of slot hashCodes.
*/
public class InventoryFingerprint {
diff --git a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeCacheKey.java b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeCacheKey.java
index 154d1b9..49fc7c1 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeCacheKey.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeCacheKey.java
@@ -9,16 +9,16 @@
public class RecipeCacheKey {
private final String recipeId;
private final byte[] inventoryHash;
- private final int playerLevel;
+ private final int playerLevel;
private final double playerMoney;
// TODO add:
/*
- * - Vanilla Exp
- * - Conditions.McMMO Map
- * - Conditions.Fabled Map
- * - Conditions.Aura Map
- * - Conditions.ProfessionLevels Map
+ * - Vanilla Exp
+ * - Conditions.McMMO Map
+ * - Conditions.Fabled Map
+ * - Conditions.Aura Map
+ * - Conditions.ProfessionLevels Map
*/
public RecipeCacheKey(String recipeId, byte[] inventoryHash, int playerLevel, double playerMoney) {
diff --git a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
index 2f9435f..ef9204a 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
@@ -29,7 +29,7 @@ public class RecipeGuiEventRouter implements Listener {
* We fetch that player’s FusionPlayer via PlayerLoader.getPlayer(Player).
*/
private RecipeGui findGuiFor(Player player, Inventory inv) {
- if(!ProfessionGuiRegistry.getLatestRecipeGui().containsKey(player.getUniqueId()))
+ if (!ProfessionGuiRegistry.getLatestRecipeGui().containsKey(player.getUniqueId()))
return null;
RecipeGui gui = ProfessionGuiRegistry.getLatestRecipeGui().get(player.getUniqueId());
if (gui.getInventory().equals(inv)) {
@@ -80,7 +80,7 @@ public void onInventoryClose(InventoryCloseEvent event) {
@EventHandler(ignoreCancelled = true)
public void onPlayerDrop(PlayerDropItemEvent event) {
- Player p = event.getPlayer();
+ Player p = event.getPlayer();
RecipeGui gui = findGuiFor(p, p.getOpenInventory().getTopInventory());
if (gui == null) return;
@@ -100,13 +100,13 @@ public void onItemPickup(EntityPickupItemEvent event) {
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
- Player p = event.getPlayer();
+ Player p = event.getPlayer();
FusionPlayer fp = PlayerLoader.getPlayer(p);
if (fp == null) return;
// On quit, close and remove *all* open RecipeGuis for that player
RecipeGui gui = ProfessionGuiRegistry.getLatestRecipeGui().get(p.getUniqueId());
- if(gui == null) return;
+ if (gui == null) return;
gui.close(p, gui.getInventory());
}
}
diff --git a/src/main/java/studio/magemonkey/fusion/gui/slot/Slot.java b/src/main/java/studio/magemonkey/fusion/gui/slot/Slot.java
index e220850..8503e36 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/slot/Slot.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/slot/Slot.java
@@ -93,7 +93,7 @@ public ItemStack canHoldItem(ItemStack item) {
/**
* -- GETTER --
- * Returns base slot type.
+ * Returns base slot type.
*
*/
protected final SlotType slotType;
diff --git a/src/test/java/studio/magemonkey/fusion/commands/ForceCommandsTest.java b/src/test/java/studio/magemonkey/fusion/commands/ForceCommandsTest.java
index 8d586c1..0503766 100644
--- a/src/test/java/studio/magemonkey/fusion/commands/ForceCommandsTest.java
+++ b/src/test/java/studio/magemonkey/fusion/commands/ForceCommandsTest.java
@@ -2,7 +2,8 @@
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
/**
* Simple test to verify force command methods exist and have the expected signatures.
@@ -16,31 +17,31 @@ public void testForceCommandMethodsExist() {
// Verify that all force command methods exist as public static methods
try {
// Check forceJoinProfession method exists
- CommandMechanics.class.getDeclaredMethod("forceJoinProfession",
- org.bukkit.command.CommandSender.class, String[].class);
-
+ CommandMechanics.class.getDeclaredMethod("forceJoinProfession",
+ org.bukkit.command.CommandSender.class, String[].class);
+
// Check forceLeaveProfession method exists
- CommandMechanics.class.getDeclaredMethod("forceLeaveProfession",
- org.bukkit.command.CommandSender.class, String[].class);
-
+ CommandMechanics.class.getDeclaredMethod("forceLeaveProfession",
+ org.bukkit.command.CommandSender.class, String[].class);
+
// Check forceStats method exists
- CommandMechanics.class.getDeclaredMethod("forceStats",
- org.bukkit.command.CommandSender.class, String[].class);
-
+ CommandMechanics.class.getDeclaredMethod("forceStats",
+ org.bukkit.command.CommandSender.class, String[].class);
+
// Check forceMaster method exists
- CommandMechanics.class.getDeclaredMethod("forceMaster",
- org.bukkit.command.CommandSender.class, String[].class);
-
+ CommandMechanics.class.getDeclaredMethod("forceMaster",
+ org.bukkit.command.CommandSender.class, String[].class);
+
// Check forceShow method exists
- CommandMechanics.class.getDeclaredMethod("forceShow",
- org.bukkit.command.CommandSender.class, String[].class);
-
+ CommandMechanics.class.getDeclaredMethod("forceShow",
+ org.bukkit.command.CommandSender.class, String[].class);
+
} catch (NoSuchMethodException e) {
fail("Force command method not found: " + e.getMessage());
}
}
- @Test
+ @Test
public void testCommandArgumentValidation() {
// This test documents expected argument patterns for force commands
// forcejoin = 3 args
@@ -48,7 +49,7 @@ public void testCommandArgumentValidation() {
// forcestats = 2 args
// forcemaster = 3 args
// forceshow = 2 args
-
+
// Test would verify argument length checking but requires mocking the full environment
assertTrue(true, "Force commands require 2-3 arguments as documented");
}
From b418ce3774da9ef0750e0c69c556696fcd8aa20c Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Sun, 14 Sep 2025 18:54:02 +0200
Subject: [PATCH 10/16] fixed categories not being openable when registered in
the gui
---
.../java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java | 1 -
.../java/studio/magemonkey/fusion/commands/Commands.java | 3 +--
.../studio/magemonkey/fusion/data/player/FusionPlayer.java | 1 +
.../fusion/data/professions/ProfessionSettings.java | 2 +-
src/main/java/studio/magemonkey/fusion/gui/CategoryGui.java | 5 ++++-
5 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java b/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
index e91be64..d1f70ee 100644
--- a/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
+++ b/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
@@ -121,7 +121,6 @@ private static void loadProfessions(File root) {
cfgs.put(ct.getName(), cfg);
files.put(ct.getName(), file);
injectProfessionLevelConfig(ct, file);
-
} catch (Exception e) {
e.printStackTrace();
Fusion.getInstance().getLogger().warning("Can't load crafting table: " + e.getMessage());
diff --git a/src/main/java/studio/magemonkey/fusion/commands/Commands.java b/src/main/java/studio/magemonkey/fusion/commands/Commands.java
index 50c5dc1..9af8c83 100644
--- a/src/main/java/studio/magemonkey/fusion/commands/Commands.java
+++ b/src/main/java/studio/magemonkey/fusion/commands/Commands.java
@@ -125,8 +125,7 @@ public List onTabComplete(@NotNull CommandSender sender,
return List.of();
List entries = new ArrayList<>();
- List professions = new ArrayList<>();
- professions = new ArrayList<>(PlayerLoader.getPlayer((player).getUniqueId()).getProfessions());
+ List professions = new ArrayList<>(PlayerLoader.getPlayer((player).getUniqueId()).getProfessions());
if (args.length == 1) {
if (sender.hasPermission("fusion.browse")
&& "browse".startsWith(args[0])) entries.add("browse");
diff --git a/src/main/java/studio/magemonkey/fusion/data/player/FusionPlayer.java b/src/main/java/studio/magemonkey/fusion/data/player/FusionPlayer.java
index bd8b9cd..465418a 100644
--- a/src/main/java/studio/magemonkey/fusion/data/player/FusionPlayer.java
+++ b/src/main/java/studio/magemonkey/fusion/data/player/FusionPlayer.java
@@ -3,6 +3,7 @@
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import studio.magemonkey.fusion.cfg.sql.SQLManager;
diff --git a/src/main/java/studio/magemonkey/fusion/data/professions/ProfessionSettings.java b/src/main/java/studio/magemonkey/fusion/data/professions/ProfessionSettings.java
index dc14c68..7a4cba6 100644
--- a/src/main/java/studio/magemonkey/fusion/data/professions/ProfessionSettings.java
+++ b/src/main/java/studio/magemonkey/fusion/data/professions/ProfessionSettings.java
@@ -274,7 +274,7 @@ private void generateIcon(String namespace) {
meta = potionMeta;
}
builder = builder.data(meta);
- this.recipeItem = new RecipeCustomItem(builder, iconReference.getAmount(), false);
+ this.recipeItem = new RecipeCustomItem(builder, 1, false);
}
}
diff --git a/src/main/java/studio/magemonkey/fusion/gui/CategoryGui.java b/src/main/java/studio/magemonkey/fusion/gui/CategoryGui.java
index 1f1a423..55924e8 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/CategoryGui.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/CategoryGui.java
@@ -36,6 +36,7 @@ public class CategoryGui implements Listener {
private final CraftingTable table;
private Inventory inventory;
+ private final Map allCategoriesMap = new HashMap<>();
private final Map categories = new HashMap<>();
private int page = 0;
private int nextPage = -1;
@@ -103,6 +104,8 @@ public void reloadCategories() {
: CodexEngine.get().getVault().getBalance(player))
});
+ allCategories.forEach((category) -> allCategoriesMap.putIfAbsent(category.getName(), new RecipeGui(player, table, category)));
+
for (int k = (page * pageSize), e = Math.min(slots.length, allCategoryArray.length);
(k < allCategoryArray.length) && (i < e);
k++, i++) {
@@ -255,7 +258,7 @@ private void nextPage() {
public void open(Player player, Category category) {
if (category == null) open(player);
else {
- for (RecipeGui gui : categories.values()) {
+ for (RecipeGui gui : allCategoriesMap.values()) {
if (gui.getCategory().equals(category)) {
gui.open(player);
return;
From d18c7967d261b7c01b72f9a2ac31927944c03034 Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Tue, 16 Sep 2025 22:38:57 +0200
Subject: [PATCH 11/16] remove latest gui on leave
---
.../magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
index ef9204a..c5de114 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/recipe/RecipeGuiEventRouter.java
@@ -108,5 +108,6 @@ public void onPlayerQuit(PlayerQuitEvent event) {
RecipeGui gui = ProfessionGuiRegistry.getLatestRecipeGui().get(p.getUniqueId());
if (gui == null) return;
gui.close(p, gui.getInventory());
+ ProfessionGuiRegistry.getLatestRecipeGui().remove(p.getUniqueId());
}
}
From 02e07a61d946b865faefd21e534da9b72f718771 Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Tue, 16 Sep 2025 22:39:28 +0200
Subject: [PATCH 12/16] caching money to reduce lags through db-connections
---
.../magemonkey/fusion/gui/RecipeGui.java | 6 +-
.../magemonkey/fusion/hook/VaultHook.java | 67 +++++++++++++++++++
2 files changed, 69 insertions(+), 4 deletions(-)
create mode 100644 src/main/java/studio/magemonkey/fusion/hook/VaultHook.java
diff --git a/src/main/java/studio/magemonkey/fusion/gui/RecipeGui.java b/src/main/java/studio/magemonkey/fusion/gui/RecipeGui.java
index 02dfcda..81d107c 100644
--- a/src/main/java/studio/magemonkey/fusion/gui/RecipeGui.java
+++ b/src/main/java/studio/magemonkey/fusion/gui/RecipeGui.java
@@ -47,12 +47,12 @@
import studio.magemonkey.fusion.gui.recipe.InventoryFingerprint;
import studio.magemonkey.fusion.gui.recipe.RecipeCacheKey;
import studio.magemonkey.fusion.gui.slot.Slot;
+import studio.magemonkey.fusion.hook.VaultHook;
import studio.magemonkey.fusion.util.ChatUT;
import studio.magemonkey.fusion.util.ExperienceManager;
import studio.magemonkey.fusion.util.PlayerUtil;
import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
@Getter
public class RecipeGui implements Listener {
@@ -264,9 +264,7 @@ public void reloadRecipes() {
//
byte[] newHash = InventoryFingerprint.fingerprint(player);
int newLevel = table.getLevelFunction().getLevel(player);
- double newMoney = (CodexEngine.get().getVault() == null)
- ? 0.0
- : CodexEngine.get().getVault().getBalance(player);
+ double newMoney = VaultHook.getBalance(player);
boolean invChanged = !Arrays.equals(newHash, lastInventoryHash);
boolean levelChanged = (newLevel != lastSeenLevel);
diff --git a/src/main/java/studio/magemonkey/fusion/hook/VaultHook.java b/src/main/java/studio/magemonkey/fusion/hook/VaultHook.java
new file mode 100644
index 0000000..029c31c
--- /dev/null
+++ b/src/main/java/studio/magemonkey/fusion/hook/VaultHook.java
@@ -0,0 +1,67 @@
+package studio.magemonkey.fusion.hook;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitTask;
+import studio.magemonkey.codex.CodexEngine;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public class VaultHook {
+
+ private static BukkitTask task;
+ private static Map> storedBalances = new HashMap<>();
+ private static int thresholdSeconds = 10;
+
+ public static double getBalance(Player player) {
+ if (storedBalances.containsKey(player.getUniqueId())) {
+ Pair balanceData = storedBalances.get(player.getUniqueId());
+ double balance = balanceData.getLeft();
+ LocalDateTime timestamp = balanceData.getRight();
+ if (timestamp.plusSeconds(thresholdSeconds).isBefore(LocalDateTime.now())) {
+ double updatedBalance = CodexEngine.get().getVault() != null ? CodexEngine.get().getVault().getBalance(player) : 0.0;
+ storedBalances.put(player.getUniqueId(), Pair.of(updatedBalance, LocalDateTime.now()));
+ return updatedBalance;
+ } else {
+ return balance;
+ }
+ } else {
+ double balance = CodexEngine.get().getVault() != null ? CodexEngine.get().getVault().getBalance(player) : 0.0;
+ storedBalances.put(player.getUniqueId(), Pair.of(balance, LocalDateTime.now()));
+ return balance;
+ }
+ }
+
+ public static void startMoneyUpdateTask() {
+ // Schedule a repeating task to update all stored balances every thresholdSeconds
+ task = Bukkit.getScheduler().runTaskTimerAsynchronously(CodexEngine.get(), () -> {
+ for (UUID uuid : storedBalances.keySet()) {
+ Player player = Bukkit.getPlayer(uuid);
+ if (player != null && player.isOnline()) {
+ getBalanceAsync(player, balance -> {
+ storedBalances.put(uuid, Pair.of(balance, LocalDateTime.now()));
+ });
+ }
+ }
+ }, thresholdSeconds * 20L, thresholdSeconds * 20L);
+ }
+
+ public static void cancelMoneyUpdateTask() {
+ if (task != null) {
+ task.cancel();
+ task = null;
+ }
+ }
+
+ private static void getBalanceAsync(Player player, Consumer moneyConsumer) {
+ Bukkit.getScheduler().runTaskAsynchronously(CodexEngine.get(), () -> {
+ double balance = CodexEngine.get().getVault() != null ? CodexEngine.get().getVault().getBalance(player) : 0.0;
+ moneyConsumer.accept(balance);
+ });
+ }
+}
From b9c341ce9e2b324948f4a7f3b5da8ea8365ae30d Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Wed, 17 Sep 2025 11:20:43 +0200
Subject: [PATCH 13/16] added debug for issue handling with queue data loss on
rejoining
---
.../studio/magemonkey/fusion/data/queue/CraftingQueue.java | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java b/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
index a7d1c53..68117a3 100644
--- a/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
+++ b/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
@@ -12,6 +12,7 @@
import studio.magemonkey.fusion.cfg.Cfg;
import studio.magemonkey.fusion.cfg.ProfessionsCfg;
import studio.magemonkey.fusion.cfg.sql.SQLManager;
+import studio.magemonkey.fusion.data.player.FusionPlayer;
import studio.magemonkey.fusion.data.player.PlayerLoader;
import studio.magemonkey.fusion.data.professions.pattern.Category;
import studio.magemonkey.fusion.data.recipes.Recipe;
@@ -50,6 +51,10 @@ public CraftingQueue(Player player, String profession, Category category) {
* queue sequentially. All items are saved with the same timestamp when
* saved, so use the first item's timestamp to calculate the offline duration.
*/
+ if(!queue.isEmpty()) {
+ Fusion.getInstance().getLogger().warning("[Debug] Loaded " + queue.size() + " items for " + player.getName() + " in " + profession + " - " + category.getName() + ": ItemPaths:" + queue.stream().map(item -> item.getRecipe().getRecipePath()).toList());
+ }
+
if (Cfg.updateQueueOffline && !queue.isEmpty()) {
long now = System.currentTimeMillis();
// find the first unfinished item
From 9aadc3e2814c8ba9981a513d39b8a94f107be620 Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Wed, 17 Sep 2025 23:38:07 +0200
Subject: [PATCH 14/16] fixed queue data loss with properly configured primary
keys
---
.../java/studio/magemonkey/fusion/Fusion.java | 4 +--
.../cfg/sql/tables/FusionQueuesSQL.java | 30 +++++--------------
2 files changed, 9 insertions(+), 25 deletions(-)
diff --git a/src/main/java/studio/magemonkey/fusion/Fusion.java b/src/main/java/studio/magemonkey/fusion/Fusion.java
index 69b0198..59eb78d 100644
--- a/src/main/java/studio/magemonkey/fusion/Fusion.java
+++ b/src/main/java/studio/magemonkey/fusion/Fusion.java
@@ -127,8 +127,8 @@ public void onEnable() {
LevelFunction.generate(200);
this.getCommand("craft").setExecutor(new Commands());
this.getCommand("fusion-editor").setExecutor(new FusionEditorCommand());
- getServer().getPluginManager().registerEvents(this, this);
- Bukkit.getPluginManager().registerEvents(new RecipeGuiEventRouter(), this);
+ registerListener(this);
+ registerListener(new RecipeGuiEventRouter());
runQueueTask();
if (hookManager.isHooked(HookType.PlaceholderAPI)) {
diff --git a/src/main/java/studio/magemonkey/fusion/cfg/sql/tables/FusionQueuesSQL.java b/src/main/java/studio/magemonkey/fusion/cfg/sql/tables/FusionQueuesSQL.java
index 9af7774..8c2050b 100644
--- a/src/main/java/studio/magemonkey/fusion/cfg/sql/tables/FusionQueuesSQL.java
+++ b/src/main/java/studio/magemonkey/fusion/cfg/sql/tables/FusionQueuesSQL.java
@@ -22,7 +22,7 @@ public class FusionQueuesSQL {
public FusionQueuesSQL() {
try (PreparedStatement create = SQLManager.connection()
.prepareStatement("CREATE TABLE IF NOT EXISTS " + Table + "("
- + "Id long,"
+ + "Id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ "UUID varchar(36), "
+ "RecipePath varchar(100),"
+ "CraftingTime numeric,"
@@ -37,33 +37,17 @@ public FusionQueuesSQL() {
}
}
- public long getNextId() {
- try (PreparedStatement select = SQLManager.connection().prepareStatement("SELECT Count(Id) FROM " + Table)) {
- ResultSet result = select.executeQuery();
- if (result.next()) {
- return result.getLong(1);
- }
- } catch (SQLException e) {
- Fusion.getInstance()
- .getLogger()
- .warning("[SQL:FusionQueuesSQL:getNextId] Something went wrong with the sql-connection: "
- + e.getMessage());
- }
- return 0;
- }
-
public boolean setQueueItem(UUID uuid, QueueItem item) {
if (item == null) return false;
if (item.getId() == -1) {
try (PreparedStatement insert = SQLManager.connection()
.prepareStatement("INSERT INTO " + Table
- + "(Id, UUID, RecipePath, Timestamp, CraftingTime, SavedSeconds) VALUES (?,?,?,?,?,?)")) {
- insert.setLong(1, getNextId());
- insert.setString(2, uuid.toString());
- insert.setString(3, item.getRecipePath());
- insert.setLong(4, item.getTimestamp());
- insert.setLong(5, item.getRecipe().getCraftingTime());
- insert.setLong(6, item.getSavedSeconds());
+ + "(UUID, RecipePath, Timestamp, CraftingTime, SavedSeconds) VALUES (?,?,?,?,?)")) {
+ insert.setString(1, uuid.toString());
+ insert.setString(2, item.getRecipePath());
+ insert.setLong(3, item.getTimestamp());
+ insert.setLong(4, item.getRecipe().getCraftingTime());
+ insert.setLong(5, item.getSavedSeconds());
insert.execute();
return true;
} catch (SQLException e) {
From 362ff2a5074532c3d477b8c8276a565a564383c0 Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Wed, 17 Sep 2025 23:38:32 +0200
Subject: [PATCH 15/16] removed debug
---
.../studio/magemonkey/fusion/data/queue/CraftingQueue.java | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java b/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
index 68117a3..def10b6 100644
--- a/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
+++ b/src/main/java/studio/magemonkey/fusion/data/queue/CraftingQueue.java
@@ -51,10 +51,6 @@ public CraftingQueue(Player player, String profession, Category category) {
* queue sequentially. All items are saved with the same timestamp when
* saved, so use the first item's timestamp to calculate the offline duration.
*/
- if(!queue.isEmpty()) {
- Fusion.getInstance().getLogger().warning("[Debug] Loaded " + queue.size() + " items for " + player.getName() + " in " + profession + " - " + category.getName() + ": ItemPaths:" + queue.stream().map(item -> item.getRecipe().getRecipePath()).toList());
- }
-
if (Cfg.updateQueueOffline && !queue.isEmpty()) {
long now = System.currentTimeMillis();
// find the first unfinished item
From 2f6666a9c071c0fdcb88c6766f38272133e53b9e Mon Sep 17 00:00:00 2001
From: MaksyKun <77341370+MaksyKun@users.noreply.github.com>
Date: Thu, 18 Sep 2025 23:04:16 +0200
Subject: [PATCH 16/16] fixed skull meta on queued items
---
.../magemonkey/fusion/cfg/ProfessionsCfg.java | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java b/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
index d1f70ee..ec69b80 100644
--- a/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
+++ b/src/main/java/studio/magemonkey/fusion/cfg/ProfessionsCfg.java
@@ -1,13 +1,16 @@
package studio.magemonkey.fusion.cfg;
import lombok.Getter;
+import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import studio.magemonkey.codex.legacy.item.ItemBuilder;
+import studio.magemonkey.codex.legacy.item.SkullBuilder;
import studio.magemonkey.fusion.Fusion;
import studio.magemonkey.fusion.cfg.migrations.ProfessionMigration;
import studio.magemonkey.fusion.data.professions.pattern.Category;
@@ -15,6 +18,7 @@
import studio.magemonkey.fusion.data.recipes.CraftingTable;
import studio.magemonkey.fusion.gui.ProfessionGuiRegistry;
import studio.magemonkey.fusion.hook.NexoHook;
+import studio.magemonkey.fusion.util.ChatUT;
import studio.magemonkey.fusion.util.Utils;
import java.io.File;
@@ -464,10 +468,17 @@ public static ItemStack getQueueItem(String key, QueueItem item) {
.warning("Profession '" + key + "' has an unknown material: " + materialString);
return new ItemStack(Material.AIR);
}
+ if(material != result.getType()) result.setType(material);
+ ItemMeta meta = result.getItemMeta();
+ if(meta != null) {
+ List lore = cfg.getStringList(path + ".lore");
+ lore.replaceAll(s -> ChatUT.hexString(s.replace("%time%", Utils.getFormattedTime(item.getVisualRemainingItemTime()))));
+ meta.setLore(lore);
+ result.setItemMeta(meta);
+ }
+
- List lore = cfg.getStringList(path + ".lore");
- lore.replaceAll(s -> s.replace("%time%", Utils.getFormattedTime(item.getVisualRemainingItemTime())));
- return ItemBuilder.newItem(result).material(material).lore(lore).build();
+ return result;
}
public static void closeAll() {