diff --git a/pom.xml b/pom.xml index 671ed74..d3ebe9b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,15 +5,15 @@ 4.0.0 CraftEnhance CraftEnhance - 2.5.6.3.2 + 2.5.7.0 CraftEnhance This is a minecraft plugin to enhance crafting. UTF-8 - 21 + 21 21 8 - 0.111 + 0.116 com.dutchjelly.craftenhance.libs @@ -77,10 +77,10 @@ jitpack.io https://jitpack.io - + codemc-repo https://repo.codemc.io/repository/maven-public/ @@ -102,7 +102,7 @@ com.mojang authlib - 3.11.49 + 3.13.56 provided diff --git a/src/main/java/com/dutchjelly/bukkitadapter/Adapter.java b/src/main/java/com/dutchjelly/bukkitadapter/Adapter.java index 1d1c80b..8fd852b 100644 --- a/src/main/java/com/dutchjelly/bukkitadapter/Adapter.java +++ b/src/main/java/com/dutchjelly/bukkitadapter/Adapter.java @@ -41,7 +41,7 @@ public class Adapter { public static List CompatibleVersions() { - return Arrays.asList("1.9", "1.10", "1.11", "1.12", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20"); + return Arrays.asList("1.9", "1.10", "1.11", "1.12", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20","1.21"); } public final static String GUI_SKULL_MATERIAL_NAME = "GUI_SKULL_ITEM"; diff --git a/src/main/java/com/dutchjelly/craftenhance/CraftEnhance.java b/src/main/java/com/dutchjelly/craftenhance/CraftEnhance.java index e02ba85..4844877 100644 --- a/src/main/java/com/dutchjelly/craftenhance/CraftEnhance.java +++ b/src/main/java/com/dutchjelly/craftenhance/CraftEnhance.java @@ -2,6 +2,7 @@ import com.dutchjelly.bukkitadapter.Adapter; import com.dutchjelly.craftenhance.api.CraftEnhanceAPI; +import com.dutchjelly.craftenhance.cache.CacheRecipes; import com.dutchjelly.craftenhance.commandhandling.CustomCmdHandler; import com.dutchjelly.craftenhance.commands.ceh.ChangeKeyCmd; import com.dutchjelly.craftenhance.commands.ceh.CleanItemFileCmd; @@ -27,6 +28,7 @@ import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.BlastRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.SmokerRecipe; import com.dutchjelly.craftenhance.crafthandling.util.ItemMatchers; +import com.dutchjelly.craftenhance.database.RecipeDatabase; import com.dutchjelly.craftenhance.files.CategoryDataCache; import com.dutchjelly.craftenhance.files.ConfigFormatter; import com.dutchjelly.craftenhance.files.FileManager; @@ -40,6 +42,7 @@ import com.dutchjelly.craftenhance.updatechecking.VersionChecker; import com.dutchjelly.craftenhance.util.Metrics; import lombok.Getter; +import lombok.NonNull; import org.broken.arrow.menu.library.RegisterMenuAPI; import org.bukkit.Bukkit; import org.bukkit.command.Command; @@ -51,17 +54,23 @@ import java.io.File; import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; public class CraftEnhance extends JavaPlugin { private static CraftEnhance plugin; - - public static CraftEnhance self() { return plugin; } + + @Getter + private SaveScheduler saveScheduler; + @Getter + private CacheRecipes cacheRecipes; + @Getter + private RecipeDatabase database; private Metrics metrics; @Getter private FileManager fm; @@ -89,6 +98,11 @@ public static CraftEnhance self() { @Override public void onEnable() { plugin = this; + + this.database = new RecipeDatabase(); + this.saveScheduler = new SaveScheduler(); + this.cacheRecipes = new CacheRecipes(this); + //The file manager needs serialization, so firstly register the classes. registerSerialization(); versionChecker = VersionChecker.init(this); @@ -98,12 +112,15 @@ public void onEnable() { saveDefaultConfig(); Debug.init(this); + if (isReloading) Bukkit.getScheduler().runTaskAsynchronously(this, () -> loadPluginData(isReloading)); else { this.loadPluginData(false); loadRecipes(); } + if (injector == null) + injector = new RecipeInjector(this); guiManager = new GuiManager(this); Debug.Send("Setting up listeners and commands"); @@ -124,6 +141,7 @@ public void onEnable() { metrics = new Metrics(this, metricsId); } CraftEnhanceAPI.registerListener(new ExecuteCommand()); + saveScheduler.start(); } @@ -148,7 +166,7 @@ public void onDisable() { fm.saveContainerOwners(injector.getContainerOwners()); Debug.Send("Saving disabled recipes..."); fm.saveDisabledServerRecipes(RecipeLoader.getInstance().getDisabledServerRecipes().stream().map(x -> Adapter.GetRecipeIdentifier(x)).collect(Collectors.toList())); - fm.getRecipes().forEach(EnhancedRecipe::save); + cacheRecipes.getRecipes().forEach(EnhancedRecipe::save); categoryDataCache.save(); } @@ -249,19 +267,24 @@ private void loadRecipes() { //Most other instances use the file manager, so setup before everything. Debug.Send("Setting up the file manager for recipes."); setupFileManager(); - Debug.Send("Loading recipes"); final RecipeLoader loader = RecipeLoader.getInstance(); - fm.getRecipes().stream().filter(x -> x.validate() == null).forEach((recipe) -> loader.loadRecipe(recipe, isReloading)); - loader.printGroupsDebugInfo(); + Bukkit.getScheduler().runTaskAsynchronously(this, ()-> { + @NonNull List recipes = this.database.loadRecipes(); + this.cacheRecipes.addAll(recipes); + Bukkit.getScheduler().runTask(this, ()->loadingRecipes(loader)); + }); + isReloading = false; + } + private void loadingRecipes(final RecipeLoader loader) { + this.cacheRecipes.getRecipes().stream().filter(x -> x.validate() == null).forEach((recipe) -> loader.loadRecipe(recipe, isReloading)); + loader.printGroupsDebugInfo(); loader.disableServerRecipes( fm.readDisabledServerRecipes().stream().map(x -> Adapter.FilterRecipes(loader.getServerRecipes(), x) ).collect(Collectors.toList()) ); - if (injector == null) - injector = new RecipeInjector(this); injector.registerContainerOwners(fm.getContainerOwners()); injector.setLoader(loader); //todo learn recipes are little broken. when you reload it. @@ -269,7 +292,6 @@ private void loadRecipes() { if (self().getConfig().getBoolean("learn-recipes")) for (final Player player : Bukkit.getOnlinePlayers()) Adapter.DiscoverRecipes(player, getCategoryDataCache().getServerRecipes()); - isReloading = false; } public void openEnhancedCraftingTable(final Player p) { diff --git a/src/main/java/com/dutchjelly/craftenhance/SaveScheduler.java b/src/main/java/com/dutchjelly/craftenhance/SaveScheduler.java new file mode 100644 index 0000000..73e97e3 --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/SaveScheduler.java @@ -0,0 +1,86 @@ +package com.dutchjelly.craftenhance; + +import com.dutchjelly.craftenhance.messaging.Debug; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static com.dutchjelly.craftenhance.CraftEnhance.self; + +public class SaveScheduler implements Runnable { + private static final long SAVE_TASK_INTERVAL = 600 * 4; + private static final long SAVE_INTERVAL = 60000 * 8; + private int taskId = -1; + private long lastSaveTime = 0; + private long lastSaveTask = 0; + private volatile boolean isRunningTask; + private BukkitTask task; + private final Queue taskQueue = new ConcurrentLinkedQueue<>(); + + public void start() { + if (task != null && !task.isCancelled()) { + return; + } + + this.task = Bukkit.getScheduler().runTaskTimerAsynchronously(self(), this, 20 * 30, 20); + taskId = this.task.getTaskId(); + } + + public void stop() { + if (taskId == -1) return; + + if (Bukkit.getScheduler().isCurrentlyRunning(taskId) && Bukkit.getScheduler().isQueued(taskId)) { + Bukkit.getScheduler().cancelTask(taskId); + } + } + + @Override + public void run() { + long now = System.currentTimeMillis(); + + if (now - lastSaveTask >= SAVE_TASK_INTERVAL && !isRunningTask) { + executeTask(); + lastSaveTask = now; + } + + if (now - lastSaveTime >= SAVE_INTERVAL) { + Debug.Send("Doing the scheduler save task."); + self().getCacheRecipes().save(); + lastSaveTime = now; + } + + } + + private void executeTask() { + if (taskQueue.isEmpty() || isRunningTask) { + return; + } + Debug.Send("Doing the batch save to the database."); + isRunningTask = true; + new BukkitRunnable() { + @Override + public void run() { + int batchSize = 4; + Debug.Send("Working with the batch of: " + taskQueue.size()); + for (int i = 0; i < batchSize && !taskQueue.isEmpty(); i++) { + Runnable nextTask = taskQueue.poll(); + if (nextTask != null) { + nextTask.run(); + } + } + if (taskQueue.isEmpty()) { + isRunningTask = false; + cancel(); + } + } + }.runTaskTimerAsynchronously(self(), 1, 5); + } + + public void addTask(Runnable task) { + taskQueue.add(task); + } + +} diff --git a/src/main/java/com/dutchjelly/craftenhance/cache/CacheRecipes.java b/src/main/java/com/dutchjelly/craftenhance/cache/CacheRecipes.java new file mode 100644 index 0000000..45bb60d --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/cache/CacheRecipes.java @@ -0,0 +1,71 @@ +package com.dutchjelly.craftenhance.cache; + +import com.dutchjelly.craftenhance.CraftEnhance; +import com.dutchjelly.craftenhance.SaveScheduler; +import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; +import com.dutchjelly.craftenhance.database.RecipeDatabase; +import com.dutchjelly.craftenhance.messaging.Debug; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CacheRecipes { + + private final List recipes = new ArrayList<>(); + private final RecipeDatabase database; + private final SaveScheduler saveSchedule; + + public CacheRecipes(final CraftEnhance craftEnhance) { + this.database = craftEnhance.getDatabase(); + this.saveSchedule = craftEnhance.getSaveScheduler(); + } + + public List getRecipes() { + return Collections.unmodifiableList(recipes); + } + + @Nullable + public EnhancedRecipe getRecipe(final String recipeName) { + for (final EnhancedRecipe recipe : recipes) { + if (recipe.getKey().equals(recipeName)) + return recipe; + } + return null; + } + + public void add(EnhancedRecipe enhancedRecipe) { + if (enhancedRecipe == null) return; + recipes.add(enhancedRecipe); + } + + public void addAll(List enhancedRecipes) { + recipes.addAll(enhancedRecipes); + } + + public void remove(EnhancedRecipe enhancedRecipe) { + EnhancedRecipe recipe = enhancedRecipe; + saveSchedule.addTask(() -> { + this.database.deleteRecipe(recipe); + }); + recipes.remove(enhancedRecipe); + } + + public boolean isUniqueRecipeKey(final String key) { + return this.getRecipe(key) == null; + } + public void clear() { + recipes.clear(); + } + + public void save() { + saveSchedule.addTask(this.database::saveRecipes); + } + + public void save(EnhancedRecipe enhancedRecipe) { + saveSchedule.addTask(() -> this.database.saveRecipe(enhancedRecipe)); + } + + +} diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/ChangeKeyCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/ChangeKeyCmd.java index 46908ec..1a11b17 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/ChangeKeyCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/ChangeKeyCmd.java @@ -29,7 +29,7 @@ public void handlePlayerCommand(Player p, String[] args) { Messenger.MessageFromConfig("messages.commands.few-arguments", p, "2"); return; } - EnhancedRecipe recipe = handler.getMain().getFm().getRecipe(args[0]); + EnhancedRecipe recipe = handler.getMain().getCacheRecipes().getRecipe(args[0]); if(recipe == null) { Messenger.Message("That recipe key doesn't exist", p); return; @@ -37,10 +37,10 @@ public void handlePlayerCommand(Player p, String[] args) { //TODO this method of changing the key changes the order of the recipes, which is a weird side-effect, so resolve this - handler.getMain().getFm().removeRecipe(recipe); + recipe.remove(); RecipeLoader.getInstance().unloadRecipe(recipe); recipe.setKey(args[1]); - handler.getMain().getFm().saveRecipe(recipe); + recipe.save(); RecipeLoader.getInstance().loadRecipe(recipe); Messenger.Message("The key has been changed to " + args[1] + ".", p); } @@ -51,15 +51,16 @@ public void handleConsoleCommand(CommandSender sender, String[] args) { Messenger.MessageFromConfig("messages.commands.few-arguments", sender, "2"); return; } - EnhancedRecipe recipe = handler.getMain().getFm().getRecipe(args[0]); + EnhancedRecipe recipe = handler.getMain().getCacheRecipes().getRecipe(args[0]); if(recipe == null) { Messenger.Message("That recipe key doesn't exist", sender); return; } - handler.getMain().getFm().removeRecipe(recipe); + + recipe.remove(); RecipeLoader.getInstance().unloadRecipe(recipe); recipe.setKey(args[1]); - handler.getMain().getFm().saveRecipe(recipe); + recipe.save(); RecipeLoader.getInstance().loadRecipe(recipe); Messenger.Message("The key has been changed to " + args[1] + ".", sender); } diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CleanItemFileCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CleanItemFileCmd.java index 1884428..09a4c3d 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CleanItemFileCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CleanItemFileCmd.java @@ -23,13 +23,13 @@ public String getDescription() { @Override public void handlePlayerCommand(Player p, String[] args) { - handler.getMain().getFm().cleanItemFile(); - Messenger.Message("Successfully cleared all unused items.", p); + //handler.getMain().getFm().cleanItemFile(); + Messenger.Message("Not used any more, as it store items inside a SQL database and not allowing duplicates. But in case it is needed it will stay a little longer.", p); } @Override public void handleConsoleCommand(CommandSender sender, String[] args) { - handler.getMain().getFm().cleanItemFile(); - Messenger.Message("Successfully cleared all unused items.", sender); + //handler.getMain().getFm().cleanItemFile(); + Messenger.Message("Not used any more, as it store items inside a SQL database and not allowing duplicates. But in case it is needed it will stay a little longer.", sender); } } diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CreateRecipeCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CreateRecipeCmd.java index 7942fe8..941252c 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CreateRecipeCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/CreateRecipeCmd.java @@ -43,7 +43,7 @@ public void handlePlayerCommand(Player p, String[] args) { //Fill in a unique key and empty permission for the user. if (args.length == 0) { int uniqueKeyIndex = 1; - while (!handler.getMain().getFm().isUniqueRecipeKey("recipe" + uniqueKeyIndex)) + while (!handler.getMain().getCacheRecipes().isUniqueRecipeKey("recipe" + uniqueKeyIndex)) uniqueKeyIndex++; /*EditorTypeSelector gui = new EditorTypeSelector(handler.getMain().getGuiManager(), handler.getMain().getGuiTemplatesFile().getTemplate(EditorTypeSelector.class), null, p, "recipe" + uniqueKeyIndex, null); handler.getMain().getGuiManager().openGUI(p, gui);*/ @@ -55,7 +55,7 @@ public void handlePlayerCommand(Player p, String[] args) { Messenger.MessageFromConfig("messages.commands.few-arguments", p, "2"); return; } - if (!handler.getMain().getFm().isUniqueRecipeKey(args[1])) { + if (!handler.getMain().getCacheRecipes().isUniqueRecipeKey(args[1])) { Messenger.Message("The specified recipe key isn't unique.", p); return; } @@ -74,25 +74,25 @@ private static void openEditor(final Player p, final String[] args) { case "workbench": recipe = new WBRecipe(permission , null, new ItemStack[9]); recipe.setKey(args[1]); - RecipeEditor recipeEditor = new RecipeEditor<>(recipe, null, null, ButtonType.ChooseWorkbenchType); + RecipeEditor recipeEditor = new RecipeEditor<>(recipe,0, null, null, ButtonType.ChooseWorkbenchType); recipeEditor.menuOpen(p); break; case "furnace": recipe = new FurnaceRecipe(permission , null, new ItemStack[1]); recipe.setKey(args[1]); - RecipeEditorFurnace editorFurnace = new RecipeEditorFurnace((FurnaceRecipe) recipe, null, null, ButtonType.ChooseWorkbenchType); + RecipeEditorFurnace editorFurnace = new RecipeEditorFurnace((FurnaceRecipe) recipe,0, null, null, ButtonType.ChooseWorkbenchType); editorFurnace.menuOpen(p); break; case "blast": recipe = new BlastRecipe(permission , null, new ItemStack[1]); recipe.setKey(args[1]); - RecipeEditorBlast editorBlast = new RecipeEditorBlast((BlastRecipe) recipe, null, null, ButtonType.ChooseWorkbenchType,true); + RecipeEditorBlast editorBlast = new RecipeEditorBlast((BlastRecipe) recipe, 0, null, null, ButtonType.ChooseWorkbenchType,true); editorBlast.menuOpen(p); break; case "smoker": recipe = new SmokerRecipe(permission , null, new ItemStack[1]); recipe.setKey(args[1]); - RecipeEditorSmoker editorSmoker = new RecipeEditorSmoker((SmokerRecipe) recipe, null, null, ButtonType.ChooseWorkbenchType,true); + RecipeEditorSmoker editorSmoker = new RecipeEditorSmoker((SmokerRecipe) recipe, 0, null, null, ButtonType.ChooseWorkbenchType,true); editorSmoker.menuOpen(p); break; default: @@ -119,7 +119,7 @@ public List handleTabCompletion(final CommandSender sender, final String tab.add("smoker"); } if (args.length == 3) { - tab.add("key"); + tab.add("permission"); } return tab; } diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RecipesCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RecipesCmd.java index d6e34c3..72c5e37 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RecipesCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RecipesCmd.java @@ -60,16 +60,16 @@ public void handlePlayerCommand(final Player p, final String[] args) { final EnhancedRecipe recipe = getRecipe(categoryData.getEnhancedRecipes(), args[2]); if (recipe instanceof WBRecipe) { if ( p.hasPermission(PermissionTypes.Edit.getPerm())) - new RecipeEditor<>((WBRecipe) recipe, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(p); + new RecipeEditor<>((WBRecipe) recipe,0, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(p); else - new RecipeViewRecipe<>(categoryData, (WBRecipe) recipe, "WBRecipeViewer").menuOpen(p); + new RecipeViewRecipe<>(categoryData, 0,(WBRecipe) recipe, "WBRecipeViewer").menuOpen(p); return; } if (recipe instanceof FurnaceRecipe) { if (p.hasPermission(PermissionTypes.Edit.getPerm())) - new RecipeEditor<>((FurnaceRecipe) recipe, categoryData,null, ButtonType.ChooseFurnaceType).menuOpen(p); + new RecipeEditor<>((FurnaceRecipe) recipe,0, categoryData,null, ButtonType.ChooseFurnaceType).menuOpen(p); else - new RecipeViewRecipe<>(categoryData, (FurnaceRecipe) recipe, "FurnaceRecipeViewer").menuOpen(p); + new RecipeViewRecipe<>(categoryData, 0,(FurnaceRecipe) recipe, "FurnaceRecipeViewer").menuOpen(p); return; } } diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RemoveRecipeCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RemoveRecipeCmd.java index 4a7734a..b638b13 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RemoveRecipeCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/RemoveRecipeCmd.java @@ -46,7 +46,7 @@ private void removeRecipe(final CommandSender sender, String[] args) { List enhancedRecipes = RecipeLoader.getInstance().getLoadedRecipes(); for (EnhancedRecipe recipe : enhancedRecipes) { if (recipe.getKey().equals(args[0])) { - self().getFm().removeRecipe(recipe); + self().getCacheRecipes().remove(recipe); RecipeLoader.getInstance().unloadRecipe(recipe); Messenger.Message("The specified recipe is removed " + recipe.getKey(), sender); return; diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SetPermissionCmd.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SetPermissionCmd.java index d0f0741..1d75495 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SetPermissionCmd.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SetPermissionCmd.java @@ -11,7 +11,7 @@ @CommandRoute(cmdPath="ceh.setpermission", perms="perms.recipe-editor") public class SetPermissionCmd implements ICommand { - private CustomCmdHandler handler; + private final CustomCmdHandler handler; public SetPermissionCmd(CustomCmdHandler handler){ this.handler = handler; @@ -28,14 +28,14 @@ public void handlePlayerCommand(Player p, String[] args) { Messenger.MessageFromConfig("messages.commands.few-arguments", p, "2"); return; } - EnhancedRecipe recipe = handler.getMain().getFm().getRecipe(args[0]); + EnhancedRecipe recipe = handler.getMain().getCacheRecipes().getRecipe(args[0]); if(recipe == null) { Messenger.Message("That recipe key doesn't exist", p); return; } recipe.setPermission(args[1]); - handler.getMain().getFm().saveRecipe(recipe); + recipe.save(); Messenger.Message("Successfully set the permissions of the recipe to " + args[1] + ".", p); } diff --git a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SpecsCommand.java b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SpecsCommand.java index 07adbe0..ef87a33 100644 --- a/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SpecsCommand.java +++ b/src/main/java/com/dutchjelly/craftenhance/commands/ceh/SpecsCommand.java @@ -28,7 +28,7 @@ public void handlePlayerCommand(Player p, String[] args) { Messenger.MessageFromConfig("messages.commands.few-arguments", p, "1"); return; } - EnhancedRecipe recipe = handler.getMain().getFm().getRecipe(args[0]); + EnhancedRecipe recipe = handler.getMain().getCacheRecipes().getRecipe(args[0]); if(recipe == null) { Messenger.Message("That recipe key doesn't exist", p); return; diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/RecipeInjector.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/RecipeInjector.java index f16a132..bf3d568 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/RecipeInjector.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/RecipeInjector.java @@ -204,7 +204,7 @@ && isViewersAllowedCraft(viewers, wbRecipe) } }); } else { - Debug.Send(Type.Crafting, () -> "This recipe deosen't contains Modeldata and will be crafted if the recipe is not cancelled."); + Debug.Send(Type.Crafting, () -> "This recipe doesn't contains Modeldata and will be crafted if the recipe is not cancelled."); final BeforeCraftOutputEvent beforeCraftOutputEvent = new BeforeCraftOutputEvent(eRecipe, wbRecipe, wbRecipe.getResult().clone()); if (beforeCraftOutputEvent.isCancelled()) { @@ -567,6 +567,7 @@ public void furnaceBreak(final BlockBreakEvent e) { private boolean isCraftingAllowedInWorld(final Location location, final EnhancedRecipe eRecipe) { final Set allowedWorlds = eRecipe.getAllowedWorlds(); + //todo Similar recipes could prevent world blocking from working. if (allowedWorlds == null || allowedWorlds.isEmpty()) return false; if (location != null) { if (location.getWorld() == null) return true; diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/EnhancedRecipe.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/EnhancedRecipe.java index 6c84167..1f0b439 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/EnhancedRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/EnhancedRecipe.java @@ -3,6 +3,8 @@ import com.dutchjelly.bukkitadapter.Adapter; import com.dutchjelly.craftenhance.CraftEnhance; import com.dutchjelly.craftenhance.crafthandling.RecipeLoader; +import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.BlastRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.SmokerRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.utility.RecipeType; import com.dutchjelly.craftenhance.crafthandling.util.IMatcher; import com.dutchjelly.craftenhance.crafthandling.util.ItemMatchers; @@ -29,7 +31,17 @@ public abstract class EnhancedRecipe extends GuiPlacable implements ConfigurationSerializable, ServerLoadable { - public EnhancedRecipe() { + protected EnhancedRecipe(EnhancedRecipe enhancedRecipe) { + this("", null, null); + this.key = enhancedRecipe.getKey(); + this.onCraftCommand = enhancedRecipe.getOnCraftCommand(); + this.allowedWorlds = enhancedRecipe.getAllowedWorlds(); + this.checkPartialMatch = enhancedRecipe.isCheckPartialMatch(); + this.matchType = enhancedRecipe.getMatchType(); + this.deserialize = enhancedRecipe.getDeserialize(); + this.id = enhancedRecipe.getId(); + this.hidden = enhancedRecipe.isHidden(); + this.serialize = enhancedRecipe.getSerialize(); } public EnhancedRecipe(final String perm, final ItemStack result, final ItemStack[] content) { @@ -54,7 +66,8 @@ protected EnhancedRecipe(final Map args) { } if (args.containsKey("oncraftcommand")) { - onCraftCommand = (String) args.get("oncraftcommand"); + final Object oncraftcommand = args.get("oncraftcommand"); + onCraftCommand = oncraftcommand instanceof Boolean ? "" : (String) oncraftcommand; } if (args.containsKey("hidden")) @@ -81,10 +94,11 @@ protected EnhancedRecipe(final Map args) { } }); this.allowedWorlds = worlds; - - setContent(new ItemStack[recipeKeys.size()]); - for (int i = 0; i < content.length; i++) { - content[i] = fm.getItem(recipeKeys.get(i)); + if (recipeKeys != null) { + setContent(new ItemStack[recipeKeys.size()]); + for (int i = 0; i < content.length; i++) { + content[i] = fm.getItem(recipeKeys.get(i)); + } } this.deserialize = args; } @@ -122,7 +136,7 @@ protected EnhancedRecipe(final Map args) { private String onCraftCommand; @Getter - private RecipeType type; + private final RecipeType type = RecipeType.WORKBENCH; @Getter @Setter @@ -135,6 +149,19 @@ protected EnhancedRecipe(final Map args) { @Setter private boolean checkPartialMatch; + public EnhancedRecipe copy() { + switch (this.getType()) { + case FURNACE: + return new FurnaceRecipe(this); + case BLAST: + return new BlastRecipe(this); + case SMOKER: + return new SmokerRecipe(this); + default: + return new WBRecipe(this); + } + } + @Nonnull @Override public Map serialize() { @@ -147,7 +174,7 @@ public Map serialize() { put("check_partial_match", checkPartialMatch); put("oncraftcommand", onCraftCommand); put("result", fm.getItemKey(result)); - put("recipe", Arrays.stream(content).map(x -> fm.getItemKey(x)).toArray(String[]::new)); + put("recipe", Arrays.stream(content).map(fm::getItemKey).toArray(String[]::new)); put("allowed_worlds", allowedWorlds != null ? new ArrayList<>(allowedWorlds) : new ArrayList<>()); if (serialize != null && !serialize.isEmpty()) putAll(serialize); @@ -168,11 +195,17 @@ public String validate() { @Override public String toString() { return "EnhancedRecipe{" + - "key='" + key + '\'' + + "recipe_name='" + key + '\'' + ", result=" + result + ", content=" + Arrays.toString(content) + - ", recipe-type=" + type + - "}"; + ", matchType=" + matchType + + ", permission='" + permission + '\'' + + ", hidden=" + hidden + + ", on_craft_command='" + onCraftCommand + '\'' + + ", type=" + this.getType() + + ", allowedWorlds=" + allowedWorlds + + ", checkPartialMatch=" + checkPartialMatch + + "} "; } public void setPermission(final String permission) { @@ -211,16 +244,28 @@ public ItemStack getDisplayItem() { } public void save() { - if (validate() == null) - CraftEnhance.self().getFm().saveRecipe(this); + if (validate() == null) { + CraftEnhance.self().getCacheRecipes().save(this); + } + } + + public void remove() { + CraftEnhance.self().getCacheRecipes().remove(this); } public void load() { RecipeLoader.getInstance().loadRecipe(this); } + public RecipeType getType() { + return type; + } + + public abstract boolean matches(ItemStack[] content); public abstract boolean matches(ItemStack[] content, IMatcher matcher); + public abstract boolean matchesBlockType(final Material blockSmelting); + } \ No newline at end of file diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/FurnaceRecipe.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/FurnaceRecipe.java index 0901da6..39adeda 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/FurnaceRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/FurnaceRecipe.java @@ -39,7 +39,9 @@ protected FurnaceRecipe(final Map args) { public FurnaceRecipe(final String perm, final ItemStack result, final ItemStack[] content) { super(perm, result, content); } - + protected FurnaceRecipe(EnhancedRecipe enhancedRecipe){ + super(enhancedRecipe); + } public static FurnaceRecipe deserialize(final Map args) { final FurnaceRecipe recipe = new FurnaceRecipe(args); recipe.duration = (int) args.get("duration"); diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/WBRecipe.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/WBRecipe.java index 488e995..7c3d8f8 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/WBRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/WBRecipe.java @@ -36,8 +36,12 @@ private WBRecipe(final Map args){ shapeless = (Boolean) args.get("shapeless"); } - public WBRecipe(){ + protected WBRecipe(EnhancedRecipe enhancedRecipe){ + super(enhancedRecipe); + } + public WBRecipe() { + super("", null, null); } diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/BlastRecipe.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/BlastRecipe.java index 844754e..2871e82 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/BlastRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/BlastRecipe.java @@ -2,6 +2,7 @@ import com.dutchjelly.bukkitadapter.Adapter; import com.dutchjelly.craftenhance.CraftEnhance; +import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.FurnaceRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.utility.RecipeType; import com.dutchjelly.craftenhance.crafthandling.util.ServerRecipeTranslator; @@ -27,6 +28,10 @@ public BlastRecipe(final String perm, final ItemStack result, final ItemStack[] super(perm, result, content); } + public BlastRecipe(final EnhancedRecipe enhancedRecipe) { + super(enhancedRecipe); + } + @Override public Recipe getServerRecipe() { return Adapter.getBlastRecipe(CraftEnhance.self(), ServerRecipeTranslator.GetFreeKey(getKey()), getResult(), getContent()[0], 100, getExp()); diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/SmokerRecipe.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/SmokerRecipe.java index 82e4eeb..31e4096 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/SmokerRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/furnace/SmokerRecipe.java @@ -2,6 +2,7 @@ import com.dutchjelly.bukkitadapter.Adapter; import com.dutchjelly.craftenhance.CraftEnhance; +import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.FurnaceRecipe; import com.dutchjelly.craftenhance.crafthandling.recipes.utility.RecipeType; import com.dutchjelly.craftenhance.crafthandling.util.ServerRecipeTranslator; @@ -26,6 +27,10 @@ public SmokerRecipe(final String perm, final ItemStack result, final ItemStack[] super(perm, result, content); } + public SmokerRecipe(final EnhancedRecipe enhancedRecipe) { + super(enhancedRecipe); + } + @Override public Recipe getServerRecipe() { return Adapter.getSmokingRecipe(CraftEnhance.self(), ServerRecipeTranslator.GetFreeKey(getKey()), getResult(), getContent()[0], 100, getExp()); diff --git a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/utility/RecipeType.java b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/utility/RecipeType.java index 473f5b0..66d4152 100644 --- a/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/utility/RecipeType.java +++ b/src/main/java/com/dutchjelly/craftenhance/crafthandling/recipes/utility/RecipeType.java @@ -46,5 +46,18 @@ public static RecipeType getType(Block block) { return null; } } + public static RecipeType getType(String type) { + if (type == null) return WORKBENCH; + RecipeType [] recipeTypes = values(); + String typeUp = type.toUpperCase(); + + for (RecipeType recipeType : recipeTypes) { + if (recipeType.name().equals(typeUp)) { + return recipeType; + } + } + + return WORKBENCH; + } } \ No newline at end of file diff --git a/src/main/java/com/dutchjelly/craftenhance/database/IngredientWrapper.java b/src/main/java/com/dutchjelly/craftenhance/database/IngredientWrapper.java new file mode 100644 index 0000000..651b0a0 --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/database/IngredientWrapper.java @@ -0,0 +1,52 @@ +package com.dutchjelly.craftenhance.database; + +public class IngredientWrapper { + private byte[] itemData; + private int slot; + private String recipeName; + private String itemName; + private RecipeIngredientType recipeIngredientType = RecipeIngredientType.INGREDIENT; + + public IngredientWrapper() { + } + + public byte[] getItemData() { + return itemData; + } + + public int getSlot() { + return slot; + } + + public void setItemData(final byte[] itemData) { + this.itemData = itemData; + } + + public void setSlot(final int slot) { + this.slot = slot; + } + + public String getRecipeName() { + return recipeName; + } + + public String getItemName() { + return itemName; + } + + public void setRecipeName(final String recipeName) { + this.recipeName = recipeName; + } + + public void setItemName(final String itemName) { + this.itemName = itemName; + } + + public RecipeIngredientType getRecipeType() { + return recipeIngredientType; + } + + public void setRecipeType(final RecipeIngredientType recipeIngredientType) { + this.recipeIngredientType = recipeIngredientType; + } + } diff --git a/src/main/java/com/dutchjelly/craftenhance/database/RecipeDatabase.java b/src/main/java/com/dutchjelly/craftenhance/database/RecipeDatabase.java new file mode 100644 index 0000000..f7b594a --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/database/RecipeDatabase.java @@ -0,0 +1,717 @@ +package com.dutchjelly.craftenhance.database; + +import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.FurnaceRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.WBRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.BlastRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.furnace.SmokerRecipe; +import com.dutchjelly.craftenhance.crafthandling.recipes.utility.RecipeType; +import com.dutchjelly.craftenhance.messaging.Debug; +import lombok.NonNull; +import org.broken.arrow.nbt.library.RegisterNbtAPI; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import static com.dutchjelly.craftenhance.CraftEnhance.self; +import static com.dutchjelly.craftenhance.messaging.Debug.Send; + +public class RecipeDatabase implements RecipeSQLQueries { + private static final String URL = "jdbc:sqlite:" + self().getDataFolder() + "/recipes.db"; + + public RecipeDatabase() { + try (Connection connection = connect()) { + createTables(connection); + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + + public void saveRecipes() { + save(); + } + + public void saveRecipe(@NonNull final EnhancedRecipe recipe) { + Send("Saving recipe '" + recipe.getKey() + "' with data: " + recipe.toString()); + try (Connection connection = connect()) { + connection.setAutoCommit(false); + try { + saveRecipe(connection, recipe); + } finally { + try { + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + Debug.error("Could not not reset back the auto commit.", e); + } + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + @NonNull + public List loadRecipes() { + try (Connection connection = connect()) { + return this.getAllRecipe(connection); + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + return new ArrayList<>(); + } + + @Nullable + public EnhancedRecipe loadRecipe(@NonNull final String recipeId) { + try (Connection connection = connect()) { + return this.getRecipe(connection, recipeId); + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + return null; + } + + public void deleteRecipe(@NonNull final EnhancedRecipe enhancedRecipe) { + Send("Removing recipe '" + enhancedRecipe.getKey() + "' with data: " + enhancedRecipe.toString()); + + try (Connection connection = connect()) { + connection.setAutoCommit(false); + deleteRecipe(connection, enhancedRecipe); + try { + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + Debug.error("Could not not reset back the auto commit.", e); + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + public void removeAllowedWorlds(@NonNull final EnhancedRecipe enhancedRecipe) { + try (Connection connection = connect()) { + connection.setAutoCommit(false); + this.removeAllowedWorld(connection, enhancedRecipe.getKey(), enhancedRecipe.getAllowedWorlds().toArray(new String[0])); + try { + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + Debug.error("Could not not reset back the auto commit.", e); + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + private void removeAllowedWorld(@NonNull final EnhancedRecipe enhancedRecipe, @NonNull final String world) { + try (Connection connection = connect()) { + removeAllowedWorld(connection, enhancedRecipe.getKey(), world); + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + public void deleteAllIngredients(@NonNull final EnhancedRecipe enhancedRecipe) { + try (Connection connection = connect()) { + connection.setAutoCommit(false); + this.deleteAllIngredients(connection, enhancedRecipe); + try { + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + Debug.error("Could not not reset back the auto commit.", e); + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + public void removeFurnaceData(@NonNull final EnhancedRecipe enhancedRecipe) { + try (Connection connection = connect()) { + this.removeFurnaceData(connection, enhancedRecipe); + try { + connection.commit(); + } catch (SQLException e) { + Debug.error("Could not not commit changes.", e); + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + public void deleteIngredient(@NonNull final EnhancedRecipe enhancedRecipe, @NonNull final ItemStack itemStack) { + boolean isResult = enhancedRecipe.getResult().isSimilar(itemStack); + int slot = enhancedRecipe.getResultSlot(); + + if (!isResult) { + slot = getSlotIngredient(enhancedRecipe, itemStack, slot); + } + if (slot < 0) return; + + try (Connection connection = connect()) { + this.deleteIngredient(connection, enhancedRecipe.getKey(), slot); + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + + private Connection connect() throws SQLException { + return DriverManager.getConnection(URL); + } + + private int updateSQL(final PreparedStatement updateItemStmt) throws SQLException { + return updateItemStmt.executeUpdate(); + } + + // Create tables + public void createTables(Connection conn) { + String createRecipesTable = " CREATE TABLE IF NOT EXISTS recipes ( " + + "id TEXT PRIMARY KEY, " + + "recipe_type TEXT NOT NULL, " + + "page INTEGER NOT NULL, " + + "slot INTEGER NOT NULL, " + + "result_slot INTEGER NOT NULL, " + + "category TEXT NOT NULL, " + + "permission TEXT, " + + "matchtype TEXT NOT NULL, " + + "hidden BOOLEAN NOT NULL," + + "check_partial_match BOOLEAN NOT NULL, " + + "on_craft_command TEXT, " + + "result_item_type TEXT NOT NULL, " + + "shapeless BOOLEAN NOT NULL, " + + "FOREIGN KEY (result_item_type) REFERENCES items(id));"; + + String createItemsTable = " CREATE TABLE IF NOT EXISTS items ( " + + "recipe_id TEXT NOT NULL, " + + "slot INTEGER NOT NULL, " + + "name TEXT NOT NULL, " + + "item_nbt BLOB NOT NULL, " + + "type TEXT NOT NULL, " + + "PRIMARY KEY (recipe_id, slot), " + + "FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE);"; + + String createIngredientsTable = " CREATE TABLE IF NOT EXISTS ingredients (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + " recipe_id TEXT NOT NULL," + + " item_id INTEGER NOT NULL," + + " FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE," + + " FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE);"; + + String createAllowedWorldsTable = " CREATE TABLE IF NOT EXISTS allowed_worlds (" + + "recipe_id TEXT NOT NULL, " + + "world TEXT NOT NULL, " + + "PRIMARY KEY (recipe_id, world), " + + "FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE);"; + + String furnaceData = " CREATE TABLE IF NOT EXISTS furnace_data (" + + "recipe_id TEXT NOT NULL, " + + "duration INTEGER NOT NULL, " + + "exp DECIMAL(10,5) NOT NULL, " + + "PRIMARY KEY (recipe_id), " + + "FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE);"; + + try (Statement stmt = conn.createStatement()) { + stmt.addBatch(createItemsTable); + stmt.addBatch(createRecipesTable); + stmt.addBatch(createAllowedWorldsTable); + stmt.addBatch(furnaceData); + stmt.executeBatch(); + } catch (SQLException e) { + Debug.error("Failed to create one of the tables", e); + } + } + + public void insertAllowedWorlds(Connection connection, String recipeId, Set allowedWorlds) throws SQLException { + if (allowedWorlds == null || allowedWorlds.isEmpty()) return; + + try (PreparedStatement updateStmt = connection.prepareStatement(UPDATE_WORLDS_SQL); + PreparedStatement insertStmt = connection.prepareStatement(INSERT_WORLDS_SQL)) { + + for (String world : allowedWorlds) { + // First try to update + updateStmt.setString(1, world); + updateStmt.setString(2, recipeId); + updateStmt.setString(3, world); + int rowsAffected = updateStmt.executeUpdate(); + + // If no rows were updated, insert instead + if (rowsAffected == 0) { + insertStmt.setString(1, recipeId); + insertStmt.setString(2, world); + insertStmt.addBatch(); + } + } + insertStmt.executeBatch(); + } + } + + // Insert a recipe + public void insertRecipe(@NonNull Connection conn, EnhancedRecipe recipe, RecipeIngredientType recipeIngredientType) { + + try (PreparedStatement pstmt = conn.prepareStatement(INSERT_RECIPES_SQL, Statement.RETURN_GENERATED_KEYS)) { + pstmt.setString(1, recipe.getKey()); + pstmt.setString(2, recipe.getType().name()); + pstmt.setInt(3, recipe.getPage()); + pstmt.setInt(4, recipe.getSlot()); + pstmt.setInt(5, recipe.getResultSlot()); + pstmt.setString(6, recipe.getRecipeCategory()); + pstmt.setString(7, recipe.getPermission()); + pstmt.setString(8, String.valueOf(recipe.getMatchType())); + pstmt.setBoolean(9, recipe.isHidden()); + pstmt.setBoolean(10, recipe.isCheckPartialMatch()); + pstmt.setString(11, recipe.getOnCraftCommand()); + pstmt.setString(12, recipeIngredientType.name()); + + + boolean isShapeless = (recipe instanceof WBRecipe) && ((WBRecipe) recipe).isShapeless(); + pstmt.setBoolean(13, isShapeless); + try { + updateSQL(pstmt); + } catch (SQLException e) { + Debug.error("Failed to insert recipe: " + recipe.getKey()); + Debug.error("Recipe data: " + recipe, e); + } + } catch (SQLException e) { + Debug.error("Failed to insert data", e); + } + } + + private void updateFurnaces(final Connection conn, final EnhancedRecipe recipe) throws SQLException { + if (!(recipe instanceof FurnaceRecipe)) return; + + FurnaceRecipe furnaceRecipe = ((FurnaceRecipe) recipe); + try (PreparedStatement furnPreparedStatement = conn.prepareStatement(SELECT_FURNACE_DATA_SQL)) { + furnPreparedStatement.setString(1, recipe.getKey()); + ResultSet set = furnPreparedStatement.executeQuery(); + if (set.next()) { + try (PreparedStatement updateFurn = conn.prepareStatement(UPDATE_FURNACE_DATA_SQL)) { + updateFurn.setInt(1, furnaceRecipe.getDuration()); + updateFurn.setFloat(2, furnaceRecipe.getExp()); + updateFurn.setString(3, recipe.getKey()); + } + } else { + try (PreparedStatement insertFurn = conn.prepareStatement(INSERT_FURNACE_DATA_SQL)) { + insertFurn.setString(1, recipe.getKey()); + insertFurn.setInt(2, furnaceRecipe.getDuration()); + insertFurn.setFloat(3, furnaceRecipe.getExp()); + } + } + } + } + + private void save() { + try (Connection connection = connect()) { + connection.setAutoCommit(false); + try { + List tempList = new ArrayList<>(self().getCacheRecipes().getRecipes()); + for (EnhancedRecipe recipe : tempList) { + saveRecipe(connection, recipe); + } + connection.commit(); + } finally { + try { + connection.setAutoCommit(true); + } catch (SQLException e) { + Debug.error("Could not not reset back the auto commit.", e); + } + } + } catch (SQLException exception) { + Debug.error("Failed to connect to database", exception); + } + } + + + // Retrieve a recipe with its ingredients + public EnhancedRecipe getRecipe(Connection conn, String recipeId) { + EnhancedRecipe recipe; + + try (PreparedStatement pstmt = conn.prepareStatement(SELECT_RECIPE_JOIN_SQL)) { + + pstmt.setString(1, recipeId); + ResultSet rs = pstmt.executeQuery(); + if (!rs.next()) return null; + + recipe = getEnhancedRecipe(conn, recipeId, rs); + return recipe; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getAllRecipe(Connection conn) { + List enhancedRecipes = new ArrayList<>(); + try (PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_RECIPE_JOIN_SQL)) { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + final EnhancedRecipe recipe = getEnhancedRecipe(conn, rs.getString("id"), rs); + enhancedRecipes.add(recipe); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + return enhancedRecipes; + } + + private EnhancedRecipe getEnhancedRecipe(final Connection conn, final String recipeId, final ResultSet rs) throws SQLException { + EnhancedRecipe recipe; + Object resultNbt = rs.getBytes("result_nbt"); + ItemStack resultItem = null; + if (resultNbt != null) { + final ItemStack[] itemStacks = deserializeItemStack((byte[]) resultNbt); + if (itemStacks != null && itemStacks.length > 0) resultItem = itemStacks[0]; + } + + Map map = new HashMap<>(); + map.put("page", rs.getInt("page")); + map.put("slot", rs.getInt("slot")); + map.put("result_slot", rs.getInt("result_slot")); + map.put("category", rs.getString("category")); + map.put("permission", rs.getString("permission")); + map.put("matchtype", rs.getString("matchtype")); + map.put("hidden", rs.getBoolean("hidden")); + map.put("check_partial_match", rs.getBoolean("check_partial_match")); + map.put("oncraftcommand", rs.getString("on_craft_command")); + map.put("shapeless", rs.getBoolean("shapeless")); + + RecipeType type = RecipeType.getType(rs.getString("recipe_type")); + if (type != null) { + switch (type) { + case FURNACE: + map.put("duration", rs.getInt("duration")); + map.put("exp", rs.getDouble("exp")); + recipe = FurnaceRecipe.deserialize(map); + break; + case BLAST: + map.put("duration", rs.getInt("duration")); + map.put("exp", rs.getDouble("exp")); + recipe = BlastRecipe.deserialize(map); + break; + case SMOKER: + map.put("duration", rs.getInt("duration")); + map.put("exp", rs.getDouble("exp")); + recipe = SmokerRecipe.deserialize(map); + break; + default: + recipe = WBRecipe.deserialize(map); + } + } else { + recipe = WBRecipe.deserialize(map); + } + + recipe.setResult(resultItem); + recipe.setKey(recipeId); + + List ingredients = getRecipeIngredients(conn, recipeId, type); + recipe.setContent(ingredients.toArray(new ItemStack[0])); + + Set allowedWorlds = getAllowedWorlds(conn, recipeId); + recipe.setAllowedWorlds(allowedWorlds); + System.out.println("recipe: " + recipe); + return recipe; + } + + private List getRecipeIngredients(Connection conn, String recipeId, final RecipeType type) throws SQLException { + int maxAmount = 9; + if (type != RecipeType.WORKBENCH) + maxAmount = 1; + List ingredients = new ArrayList<>(Collections.nCopies(maxAmount, null)); + + try (PreparedStatement pstmt = conn.prepareStatement(SELECT_INGREDIENTS_SQL)) { + pstmt.setString(1, recipeId); + pstmt.setString(2, RecipeIngredientType.INGREDIENT.name()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + byte[] nbtData = rs.getBytes("item_nbt"); + ItemStack[] items = deserializeItemStack(nbtData); + int slot = rs.getInt("slot"); + if (slot >= 0 && slot < maxAmount && items != null && items.length > 0) { + ingredients.set(slot, items[0]); + } + } + } + return ingredients; + } + + private Set getAllowedWorlds(Connection conn, String recipeId) throws SQLException { + Set worlds = new HashSet<>(); + + try (PreparedStatement pstmt = conn.prepareStatement(SELECT_WORLDS_SQL)) { + pstmt.setString(1, recipeId); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) { + worlds.add(rs.getString("world")); + } + } + return worlds; + } + + public void deleteRecipe(Connection connection, EnhancedRecipe enhancedRecipe) throws SQLException { + String sql = "DELETE FROM recipes WHERE id = ?;"; + this.deleteAllIngredients(connection, enhancedRecipe); + this.removeAllowedWorld(connection, enhancedRecipe.getKey(), enhancedRecipe.getAllowedWorlds().toArray(new String[0])); + this.removeFurnaceData(connection, enhancedRecipe); + + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setString(1, enhancedRecipe.getKey()); + int rowsAffected = this.updateSQL(pstmt); + if (rowsAffected > 0) { + Send("Recipe '" + enhancedRecipe.getKey() + "' deleted successfully."); + } else { + Send("Recipe '" + enhancedRecipe.getKey() + "' not found."); + } + } + } + + private void removeFurnaceData(final Connection connection, final EnhancedRecipe enhancedRecipe) throws SQLException { + if (!(enhancedRecipe instanceof FurnaceRecipe)) return; + + try (PreparedStatement pstmt = connection.prepareStatement(DELETE_FURNACE_DATA_SQL)) { + pstmt.setString(1, enhancedRecipe.getKey()); + int rowsAffected = this.updateSQL(pstmt); + } + } + + + private void removeAllowedWorld(Connection connection, String recipeId, String... worlds) throws SQLException { + if (worlds == null || worlds.length == 0) return; + boolean batchMode = worlds.length > 1; + + try (PreparedStatement pstmt = connection.prepareStatement(DELETE_WORLD_SQL)) { + for (String world : worlds) { + pstmt.setString(1, recipeId); + pstmt.setString(2, world); + if (batchMode) { + pstmt.addBatch(); + } else { + int rowsAffected = pstmt.executeUpdate(); + } + } + if (batchMode) { + int[] rowsAffected = pstmt.executeBatch(); + } + } + } + + private void deleteIngredient(Connection conn, String recipeId, Integer... slots) throws SQLException { + if (slots == null || slots.length == 0) return; + + String sql = "DELETE FROM items WHERE recipe_id = ? AND slot = ?;"; + boolean batchMode = slots.length > 1; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + for (int slot : slots) { + pstmt.setString(1, recipeId); + pstmt.setInt(2, slot); + + if (batchMode) { + pstmt.addBatch(); // Add to batch + } else { + int rowsAffected = pstmt.executeUpdate(); // Execute immediately if not in batch mode + logDeletionResult(recipeId, rowsAffected); + } + } + if (batchMode) { + int[] results = pstmt.executeBatch(); + for (int rowsAffected : results) { + logDeletionResult(recipeId, rowsAffected); + } + } + } + } + + // Example usage + + private String getItemKey(final ItemStack item) { + if (item == null) return null; + String base = item.getItemMeta() != null && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().name(); + base = base.replaceAll("\\.", "_"); + return base; + } + + public void saveRecipe(Connection connection, EnhancedRecipe recipe) { + + try (PreparedStatement selectRecipeStmt = connection.prepareStatement(SELECT_RECIPE_SQL)) { + + final String recipeName = recipe.getKey(); + selectRecipeStmt.setString(1, recipeName); + + try (ResultSet recipeResultSet = selectRecipeStmt.executeQuery()) { + if (recipeResultSet.next()) { + updateRecipe(connection, recipe, recipeResultSet); + } else { + insertRecipe(connection, recipe, RecipeIngredientType.RESULT); + } + insertOrUpdateItem(connection, ingredients -> { + ingredients.setSlot(recipe.getResultSlot()); + ingredients.setRecipeName(recipe.getKey()); + final ItemStack result = recipe.getResult(); + ingredients.setItemData(serializeItemStack(new ItemStack[]{result})); + ingredients.setItemName(this.getItemKey(result)); + ingredients.setRecipeType(RecipeIngredientType.RESULT); + }); + } + this.updateFurnaces(connection, recipe); + + ItemStack[] itemStacks = recipe.getContent(); + for (int i = 0; i < itemStacks.length; i++) { + ItemStack item = itemStacks[i]; + String itemName = getItemKey(item); + final int slot = i; + insertOrUpdateItem(connection, ingredients -> { + ingredients.setSlot(slot); + ingredients.setItemData(serializeItemStack(new ItemStack[]{item})); + ingredients.setRecipeName(recipeName); + ingredients.setItemName(itemName); + }); + } + this.insertAllowedWorlds(connection, recipeName, recipe.getAllowedWorlds()); + } catch (SQLException e) { + try { + connection.rollback(); + } catch (SQLException rollbackEx) { + Debug.error("Could not rollback changes to the database", rollbackEx); + } + Debug.error("Failed to save this recipe " + recipe.getKey() + ". It will now rollback all changes made", e); + } + } + + private void updateRecipe(final Connection connection, final EnhancedRecipe recipe, final ResultSet recipeResultSet) throws SQLException { + + String resultItemType = recipeResultSet.getString("result_item_type"); + + try (PreparedStatement pstmt = connection.prepareStatement(UPDATE_RECIPE_SQL)) { + pstmt.setInt(1, recipe.getPage()); + pstmt.setString(2, recipe.getType().name()); + pstmt.setInt(3, recipe.getSlot()); + pstmt.setInt(4, recipe.getResultSlot()); + pstmt.setString(5, recipe.getRecipeCategory()); + pstmt.setString(6, recipe.getPermission()); + pstmt.setString(7, String.valueOf(recipe.getMatchType())); + pstmt.setBoolean(8, recipe.isHidden()); + pstmt.setBoolean(9, recipe.isCheckPartialMatch()); + pstmt.setString(10, recipe.getOnCraftCommand()); + pstmt.setString(11, resultItemType); + + boolean isShapeless = (recipe instanceof WBRecipe) && ((WBRecipe) recipe).isShapeless(); + pstmt.setBoolean(12, isShapeless); + + pstmt.setString(13, recipeResultSet.getString("id")); + updateSQL(pstmt); + } + } + + private void deleteAllIngredients(final Connection connection, final EnhancedRecipe enhancedRecipe) throws SQLException { + int resultSlot = enhancedRecipe.getResultSlot(); + List slots = new ArrayList<>(); + slots.add(resultSlot); + slots.addAll(getAllIngredientSlots(enhancedRecipe)); + this.deleteIngredient(connection, enhancedRecipe.getKey(), slots.toArray(new Integer[0])); + } + + public void insertOrUpdateItem(Connection conn, Consumer callback) { + if (callback == null) return; + + IngredientWrapper ingredientWrapper = new IngredientWrapper(); + callback.accept(ingredientWrapper); + final int slot = ingredientWrapper.getSlot(); + final String recipeName = ingredientWrapper.getRecipeName(); + final String itemName = ingredientWrapper.getItemName(); + byte[] nbtData = ingredientWrapper.getItemData(); + RecipeIngredientType recipeIngredientType = ingredientWrapper.getRecipeType(); + + + if (itemName == null || nbtData == null) return; + + try (PreparedStatement checkStmt = conn.prepareStatement(CHECK_ITEM_EXISTENCE_SQL)) { + checkStmt.setString(1, recipeName); + checkStmt.setInt(2, slot); + ResultSet rs = checkStmt.executeQuery(); + + if (rs.next()) { + try (PreparedStatement updateStmt = conn.prepareStatement(UPDATE_ITEM_SQL)) { + updateStmt.setBytes(1, nbtData); + updateStmt.setString(2, recipeName); + updateStmt.setInt(3, slot); + updateSQL(updateStmt); + rs.getInt(1); + } + } else { + try (PreparedStatement insertStmt = conn.prepareStatement(INSERT_ITEM_SQL, Statement.RETURN_GENERATED_KEYS)) { + insertStmt.setString(1, recipeName); + insertStmt.setInt(2, slot); + insertStmt.setString(3, itemName); + insertStmt.setBytes(4, nbtData); + insertStmt.setString(5, recipeIngredientType.name()); + updateSQL(insertStmt); + + ResultSet insertRs = insertStmt.getGeneratedKeys(); + if (insertRs.next()) { + insertRs.getInt(1); + } + } + } + } catch (SQLException e) { + Debug.error("Failed to insert or update the itemstack", e); + } + } + + private void logDeletionResult(String recipeId, int rowsAffected) { + if (rowsAffected > 0) { + Send("Ingredient removed from recipe " + recipeId); + } else { + Send("Ingredient not found in recipe " + recipeId); + } + } + + private int getSlotIngredient(final EnhancedRecipe recipe, final ItemStack itemStack, int slot) { + final ItemStack[] content = recipe.getContent(); + for (int i = 0; i < content.length; i++) { + ItemStack item = content[i]; + if (itemStack.isSimilar(item)) { + slot = i; + break; + } + } + return slot; + } + + private List getAllIngredientSlots(final EnhancedRecipe recipe) { + List slots = new ArrayList<>(); + final ItemStack[] content = recipe.getContent(); + for (int i = 0; i < content.length; i++) { + ItemStack item = content[i]; + if (item != null) { + slots.add(i); + } + } + return slots; + } + + @Nonnull + public byte[] serializeItemStack(final ItemStack[] itemStacks) { + return RegisterNbtAPI.serializeItemStack(itemStacks); + } + + @Nullable + public ItemStack[] deserializeItemStack(final byte[] itemStacks) { + return RegisterNbtAPI.deserializeItemStack(itemStacks); + } + +} diff --git a/src/main/java/com/dutchjelly/craftenhance/database/RecipeIngredientType.java b/src/main/java/com/dutchjelly/craftenhance/database/RecipeIngredientType.java new file mode 100644 index 0000000..c74d54a --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/database/RecipeIngredientType.java @@ -0,0 +1,23 @@ +package com.dutchjelly.craftenhance.database; + +public enum RecipeIngredientType { + + RESULT, + INGREDIENT; + + + public static RecipeIngredientType getType(String type) { + if (type == null) return INGREDIENT; + + RecipeIngredientType[] recipeIngredientTypes = values(); + String typeUp = type.toUpperCase(); + + for (RecipeIngredientType recipeIngredientType : recipeIngredientTypes) { + if (recipeIngredientType.name().equals(typeUp)) { + return recipeIngredientType; + } + } + + return INGREDIENT; + } +} diff --git a/src/main/java/com/dutchjelly/craftenhance/database/RecipeSQLQueries.java b/src/main/java/com/dutchjelly/craftenhance/database/RecipeSQLQueries.java new file mode 100644 index 0000000..c935e41 --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/database/RecipeSQLQueries.java @@ -0,0 +1,60 @@ +package com.dutchjelly.craftenhance.database; + +public interface RecipeSQLQueries { + + String SELECT_RECIPE_SQL = "SELECT id, recipe_type, page, slot, result_slot, category, permission, matchtype, hidden, check_partial_match, on_craft_command, result_item_type, shapeless FROM recipes WHERE id = ?"; + String CHECK_ITEM_EXISTENCE_SQL = "SELECT slot FROM items WHERE recipe_id = ? AND slot = ?;"; + String SELECT_ITEM_FROM_RECIPE_SLOT_SQL = "SELECT recipe_id, slot FROM items WHERE recipe_id = ? AND slot = ?;"; + String SELECT_FURNACE_DATA_SQL = "SELECT recipe_id, slot FROM items WHERE recipe_id = ?;"; + String SELECT_RECIPE_JOIN_SQL = "SELECT r.*, i.item_nbt AS result_nbt, furn.duration, furn.exp " + + "FROM recipes r " + + "JOIN items i ON r.result_item_type = i.type AND r.id = i.recipe_id " + + "JOIN furnace_data furn ON r.id = furn.recipe_id" + + "WHERE r.id = ?;"; + + String SELECT_ALL_RECIPE_JOIN_SQL = "SELECT r.*, i.item_nbt AS result_nbt, furn.duration, furn.exp " + + "FROM recipes r " + + "JOIN items i ON r.result_item_type = i.type AND r.id = i.recipe_id" + + "JOIN furnace_data furn ON r.id = furn.recipe_id" + + ";"; + String SELECT_INGREDIENTS_SQL = "SELECT i.item_nbt, i.slot " + + "FROM items i " + + "WHERE i.recipe_id = ? " + + "AND i.slot BETWEEN 0 AND 8 " + + "AND i.type = ? " + + "ORDER BY i.slot;"; + String SELECT_WORLDS_SQL = "SELECT world FROM allowed_worlds WHERE recipe_id = ?;"; + + String UPDATE_FURNACE_DATA_SQL = "UPDATE furnace_data SET duration = ?, exp = ? WHERE recipe_id = ?;"; + String UPDATE_ITEM_SQL = "UPDATE items SET item_nbt = ? WHERE recipe_id = ? and slot = ?;"; + + String UPDATE_RECIPE_SQL = "UPDATE recipes " + + "SET page = ?, " + + " recipe_type = ?, " + + " slot = ?, " + + " result_slot = ?, " + + " category = ?, " + + " permission = ?, " + + " matchtype = ?, " + + " hidden = ?, " + + " check_partial_match = ?, " + + " on_craft_command = ?, " + + " result_item_type = ?, " + + " shapeless = ? " + + "WHERE id = ?;"; + String UPDATE_WORLDS_SQL = "UPDATE allowed_worlds SET world = ? WHERE recipe_id = ? AND world = ?"; + + String INSERT_RECIPES_SQL = "INSERT INTO recipes " + + "(id, recipe_type, page, slot, result_slot, category, permission, matchtype, hidden, check_partial_match, on_craft_command, result_item_type, shapeless) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + String INSERT_OR_REPLACE_WORLDS_SQL = "INSERT INTO allowed_worlds (recipe_id, world) VALUES (?, ?) " + + "ON CONFLICT(recipe_id, world) DO UPDATE SET world = excluded.world;"; + + String INSERT_WORLDS_SQL = "INSERT INTO allowed_worlds (recipe_id, world) VALUES (?, ?);"; + String INSERT_ITEM_SQL = "INSERT INTO items (recipe_id, slot, name, item_nbt, type) VALUES (?, ?, ?, ?, ?);"; + + String INSERT_FURNACE_DATA_SQL = "INSERT INTO furnace_data (recipe_id, duration, exp) VALUES (?, ?, ?);"; + String DELETE_FURNACE_DATA_SQL = "DELETE FROM furnace_data WHERE recipe_id = ?;"; + + String DELETE_WORLD_SQL = "DELETE FROM allowed_worlds WHERE recipe_id = ? AND world = ?;"; +} \ No newline at end of file diff --git a/src/main/java/com/dutchjelly/craftenhance/files/CategoryData.java b/src/main/java/com/dutchjelly/craftenhance/files/CategoryData.java index e637142..6f2b6ea 100644 --- a/src/main/java/com/dutchjelly/craftenhance/files/CategoryData.java +++ b/src/main/java/com/dutchjelly/craftenhance/files/CategoryData.java @@ -50,10 +50,10 @@ public ItemStack getRecipeCategoryItem() { public List getEnhancedRecipes() { return enhancedRecipes; } - public List getEnhancedRecipes(final String recipeSeachFor) { - if (recipeSeachFor == null || recipeSeachFor.equals("")) + public List getEnhancedRecipes(final String recipeSearchFor) { + if (recipeSearchFor == null || recipeSearchFor.equals("")) return enhancedRecipes; - return enhancedRecipes.stream().filter(x -> x.getKey().contains(recipeSeachFor)).collect(Collectors.toList()); + return enhancedRecipes.stream().filter(x -> x.getKey().contains(recipeSearchFor)).collect(Collectors.toList()); } public void addEnhancedRecipes(final EnhancedRecipe enhancedRecipes) { this.enhancedRecipes.add(enhancedRecipes); diff --git a/src/main/java/com/dutchjelly/craftenhance/files/FileManager.java b/src/main/java/com/dutchjelly/craftenhance/files/FileManager.java index 9971827..225e5a5 100644 --- a/src/main/java/com/dutchjelly/craftenhance/files/FileManager.java +++ b/src/main/java/com/dutchjelly/craftenhance/files/FileManager.java @@ -1,5 +1,6 @@ package com.dutchjelly.craftenhance.files; +import com.dutchjelly.craftenhance.cache.CacheRecipes; import com.dutchjelly.craftenhance.CraftEnhance; import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; import com.dutchjelly.craftenhance.messaging.Debug; @@ -49,6 +50,7 @@ public class FileManager { private Map items; private List recipes; + private CacheRecipes cacheRecipes; private FileManager(final boolean useJson) { this.useJson = useJson; @@ -60,10 +62,11 @@ public static FileManager init(final CraftEnhance main) { fm.items = new HashMap<>(); fm.recipes = new ArrayList<>(); fm.logger = main.getLogger(); + fm.cacheRecipes = main.getCacheRecipes(); fm.dataFolder = main.getDataFolder(); fm.dataFolder.mkdir(); - fm.itemsFile = fm.getFile(fm.useJson ? "items.json" : "items.yml"); - fm.recipesFile = fm.getFile("recipes.yml"); + fm.itemsFile = new File(fm.dataFolder, fm.useJson ? "items.json" : "items.yml"); + fm.recipesFile = new File(fm.dataFolder,"recipes.yml"); fm.serverRecipeFile = fm.getFile("server-recipes.yml"); fm.containerOwnerFile = fm.getFile("container-owners.yml"); return fm; @@ -126,6 +129,9 @@ private FileConfiguration getYamlConfig(final File file) { public void cacheRecipes() { Debug.Send("The file manager is caching recipes..."); EnhancedRecipe keyValue; + if(!recipesFile.exists()) + return; + recipesConfig = getYamlConfig(recipesFile); recipes.clear(); for (final String key : recipesConfig.getKeys(false)) { @@ -138,12 +144,18 @@ public void cacheRecipes() { continue; } keyValue.setKey(key); - recipes.add(keyValue); + cacheRecipes.add(keyValue); + //recipes.add(keyValue); } + + cacheRecipes.save(); + this.recipesFile.renameTo(new File(dataFolder, "recipe_copy.yml")); } @SneakyThrows public void cacheItems() { + if(!itemsFile.exists()) + return; if (useJson) { @@ -159,17 +171,19 @@ public void cacheItems() { final Map> serialized = gson.fromJson(json.toString(), typeToken); if (serialized != null) serialized.keySet().forEach(x -> items.put(x, ItemStack.deserialize(serialized.get(x)))); - return; + } else { + if (itemsConfig == null) + itemsConfig = new YamlConfiguration(); + itemsConfig.load(itemsFile); + //itemsConfig = getYamlConfig(itemsFile); + items.clear(); + if (itemsConfig != null) + for (final String key : itemsConfig.getKeys(false)) { + items.put(key, itemsConfig.getItemStack(key)); + } } - if (itemsConfig == null) - itemsConfig = new YamlConfiguration(); - itemsConfig.load(itemsFile); - //itemsConfig = getYamlConfig(itemsFile); - items.clear(); - if (itemsConfig != null) - for (final String key : itemsConfig.getKeys(false)) { - items.put(key, itemsConfig.getItemStack(key)); - } + + this.itemsFile.renameTo(new File(dataFolder, useJson ? "items-copy.json" : "items-copy.yml")); } public Map getItems() { diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/CategoryList.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/CategoryList.java index 9839006..ebe8e4a 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/CategoryList.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/CategoryList.java @@ -96,7 +96,7 @@ public boolean run(final MenuButtonData value, final Inventory menu, final Playe return true; } if (value.isActionTypeEqual(ButtonType.Back.name())) { - new RecipeSettings<>(this.recipe, this.categoryData, null, editorType) + new RecipeSettings<>(this.recipe,0 ,this.categoryData, null, editorType) .menuOpen(player); //new RecipeEditor<>(this.recipe, this.categoryData, null, editorType).menuOpen(player); } @@ -144,7 +144,7 @@ public FillMenuButton createFillMenuButton() { return ButtonUpdateAction.NONE; } } - new RecipeSettings<>(recipe, categoryData, null, editorType).menuOpen(player); + new RecipeSettings<>(recipe, 0,categoryData, null, editorType).menuOpen(player); } return ButtonUpdateAction.NONE; }, (slot, containerData) -> { diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/EditorTypeSelector.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/EditorTypeSelector.java index 3977ee2..1b25f87 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/EditorTypeSelector.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/EditorTypeSelector.java @@ -47,11 +47,11 @@ public EditorTypeSelector(final String recipeKey, final String permission) { } private String getFreshKey(String keySeed) { - if (keySeed == null || !self().getFm().isUniqueRecipeKey(keySeed)) { + if (keySeed == null || !self().getCacheRecipes().isUniqueRecipeKey(keySeed)) { int uniqueKeyIndex = 1; keySeed = "recipe"; - while (!self().getFm().isUniqueRecipeKey(keySeed + uniqueKeyIndex)) uniqueKeyIndex++; + while (!self().getCacheRecipes().isUniqueRecipeKey(keySeed + uniqueKeyIndex)) uniqueKeyIndex++; keySeed += uniqueKeyIndex; } return keySeed; @@ -87,7 +87,7 @@ public void run(final MenuButtonData value, final Player player) { if (value.isActionTypeEqual( ButtonType.ChooseWorkbenchType.name())){ EnhancedRecipe newRecipe = new WBRecipe(permission, null, new ItemStack[9]); newRecipe.setKey(getFreshKey(recipeKey)); - final RecipeEditor recipeEditor = new RecipeEditor<>(newRecipe, null,permission, + final RecipeEditor recipeEditor = new RecipeEditor<>(newRecipe,0, null,permission, value.isActionTypeEqual(ButtonType.ChooseFurnaceType.name()) ? ButtonType.ChooseFurnaceType: ButtonType.ChooseWorkbenchType); recipeEditor.menuOpen(player); return; @@ -95,19 +95,19 @@ public void run(final MenuButtonData value, final Player player) { if (value.isActionTypeEqual( ButtonType.ChooseFurnaceType.name())){ FurnaceRecipe furnaceRecipe = new FurnaceRecipe(permission, null, new ItemStack[1]); furnaceRecipe.setKey(getFreshKey(recipeKey)); - new RecipeEditorFurnace(furnaceRecipe,null,permission,ButtonType.ChooseFurnaceType,true).menuOpen(player); + new RecipeEditorFurnace(furnaceRecipe,0,null,permission,ButtonType.ChooseFurnaceType,true).menuOpen(player); return; } if (value.isActionTypeEqual( ButtonType.ChooseBlastType.name())){ BlastRecipe blastRecipe = new BlastRecipe(permission, null, new ItemStack[1]); blastRecipe.setKey(getFreshKey(recipeKey)); - new RecipeEditorBlast(blastRecipe,null,permission,ButtonType.ChooseBlastType,true).menuOpen(player); + new RecipeEditorBlast(blastRecipe,0,null,permission,ButtonType.ChooseBlastType,true).menuOpen(player); return; } if (value.isActionTypeEqual( ButtonType.ChooseSmokerType.name())){ SmokerRecipe blastRecipe = new SmokerRecipe(permission, null, new ItemStack[1]); blastRecipe.setKey(getFreshKey(recipeKey)); - new RecipeEditorSmoker(blastRecipe,null,permission,ButtonType.ChooseSmokerType,true).menuOpen(player); + new RecipeEditorSmoker(blastRecipe,0,null,permission,ButtonType.ChooseSmokerType,true).menuOpen(player); } } } diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/RecipesViewer.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/RecipesViewer.java index f6d8a0c..6d7c007 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/RecipesViewer.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/RecipesViewer.java @@ -16,6 +16,7 @@ import com.dutchjelly.craftenhance.gui.util.ButtonType; import com.dutchjelly.craftenhance.gui.util.GuiUtil; import com.dutchjelly.craftenhance.prompt.HandleChatInput; +import com.dutchjelly.craftenhance.util.PaginatedItems; import com.dutchjelly.craftenhance.util.PermissionTypes; import org.broken.arrow.menu.button.manager.library.utility.MenuButtonData; import org.broken.arrow.menu.button.manager.library.utility.MenuTemplate; @@ -33,15 +34,15 @@ import java.util.Map.Entry; import static com.dutchjelly.craftenhance.CraftEnhance.self; -import static com.dutchjelly.craftenhance.gui.util.FormatListContents.canSeeRecipes; public class RecipesViewer extends MenuHolderPage { private final MenuSettingsCache menuSettingsCache = self().getMenuSettingsCache(); private final MenuTemplate menuTemplate; private final CategoryData categoryData; - public RecipesViewer(final CategoryData categoryData, final String recipeSeachFor, final Player player) { - super(canSeeRecipes(categoryData.getEnhancedRecipes(recipeSeachFor), player)); + public RecipesViewer(final CategoryData categoryData, final String recipeSearchFor, final Player player) { + //super(canSeeRecipes(categoryData.getEnhancedRecipes(recipeSearchFor), player)); + super(new PaginatedItems(categoryData,self().getMenuSettingsCache().getTemplate("RecipesViewer")).retrieveList(player,recipeSearchFor) ); this.menuTemplate = menuSettingsCache.getTemplate("RecipesViewer"); this.categoryData = categoryData; setFillSpace(this.menuTemplate.getFillSlots()); @@ -127,26 +128,26 @@ public FillMenuButton createFillMenuButton() { if (enhancedRecipe instanceof WBRecipe) { if (allowClick) - new RecipeEditor<>((WBRecipe) enhancedRecipe, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); - else new RecipeViewRecipe<>(categoryData, (WBRecipe) enhancedRecipe, "WBRecipeViewer").menuOpen(player); + new RecipeEditor<>((WBRecipe) enhancedRecipe,this.getPageNumber(), categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); + else new RecipeViewRecipe<>(categoryData, this.getPageNumber(),(WBRecipe) enhancedRecipe, "WBRecipeViewer").menuOpen(player); } if (enhancedRecipe instanceof FurnaceRecipe) { if (allowClick) - new RecipeEditorFurnace((FurnaceRecipe) enhancedRecipe, categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); + new RecipeEditorFurnace((FurnaceRecipe) enhancedRecipe, this.getPageNumber(),categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); else - new RecipeViewRecipe<>(categoryData, (FurnaceRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); + new RecipeViewRecipe<>(categoryData, this.getPageNumber(),(FurnaceRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); } if (enhancedRecipe instanceof BlastRecipe) { if (allowClick) - new RecipeEditorBlast((BlastRecipe) enhancedRecipe, categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); + new RecipeEditorBlast((BlastRecipe) enhancedRecipe, this.getPageNumber(),categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); else - new RecipeViewRecipe<>(categoryData, (BlastRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); + new RecipeViewRecipe<>(categoryData,this.getPageNumber(), (BlastRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); } if (enhancedRecipe instanceof SmokerRecipe) { if (allowClick) - new RecipeEditorSmoker((SmokerRecipe) enhancedRecipe, categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); + new RecipeEditorSmoker((SmokerRecipe) enhancedRecipe, this.getPageNumber(),categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); else - new RecipeViewRecipe<>(categoryData, (SmokerRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); + new RecipeViewRecipe<>(categoryData, this.getPageNumber(),(SmokerRecipe) enhancedRecipe, "FurnaceRecipeViewer").menuOpen(player); } return ButtonUpdateAction.NONE; }, (slot, enhancedRecipe) -> { diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditor.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditor.java index e760d1d..22124b7 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditor.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditor.java @@ -49,6 +49,7 @@ public class RecipeEditor extends MenuHolderPage private final MenuSettingsCache menuSettingsCache = self().getMenuSettingsCache(); private final IngredientsCache ingredientsCache; private final boolean checkPartialMatch; + private final int page; private String permission; @Getter private final RecipeT recipe; @@ -62,12 +63,13 @@ public class RecipeEditor extends MenuHolderPage private final ButtonType editorType; private final CategoryData categoryData; - public RecipeEditor(final RecipeT recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { - this(recipe, categoryData, permission, editorType, true); + public RecipeEditor(final RecipeT recipe, final int page, final CategoryData categoryData, final String permission, final ButtonType editorType) { + this(recipe, page, categoryData, permission, editorType, true); } - public RecipeEditor(final RecipeT recipe, final CategoryData categoryData, final String permission, final ButtonType editorType, final boolean clearItems) { + public RecipeEditor(final RecipeT recipe, final int page, final CategoryData categoryData, final String permission, final ButtonType editorType, final boolean clearItems) { super(formatRecipes(recipe, self().getIngredientsCache(), !clearItems)); + this.page = page; if (permission == null || permission.equals("")) this.permission = recipe.getPermission(); else this.permission = permission; @@ -142,17 +144,21 @@ public ItemStack getItem() { public boolean run(final MenuButtonData value, final Inventory menu, final Player player, final ClickType click) { if (value.isActionTypeEqual(ButtonType.DeleteRecipe.name())) { - self().getFm().removeRecipe(recipe); + self().getCacheRecipes().remove(recipe); RecipeLoader.getInstance().unloadRecipe(recipe); - if (this.categoryData != null) - new RecipesViewer(this.categoryData, "", player).menuOpen(player); + if (this.categoryData != null) { + final RecipesViewer recipesViewer = new RecipesViewer(this.categoryData, "", player); + recipesViewer.menuOpen(player); + if ( this.page > 0) + recipesViewer.setPage( this.page); + } else new EditorTypeSelector(null, permission).menuOpen(player); return true; } if (value.isActionTypeEqual(ButtonType.RecipeSettings.name())) { if (recipe instanceof WBRecipe) - new RecipeSettings<>(this.recipe, this.categoryData, this.permission, this.editorType) + new RecipeSettings<>(this.recipe, this.page, this.categoryData, this.permission, this.editorType) .menuOpen(player); } @@ -168,22 +174,25 @@ public boolean run(final MenuButtonData value, final Inventory menu, final Playe final Map map = checkItemsInsideInventory.getItemsFromSetSlots(menu, player, false); save(map, player, true); if (this.recipe instanceof WBRecipe) { - new RecipeEditor<>((WBRecipe) this.recipe, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); + new RecipeEditor<>((WBRecipe) this.recipe, this.page, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); } if (this.recipe instanceof FurnaceRecipe) { - new RecipeEditorFurnace((FurnaceRecipe) this.recipe, categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); + new RecipeEditorFurnace((FurnaceRecipe) this.recipe, this.page,categoryData, null, ButtonType.ChooseFurnaceType, false).menuOpen(player); } if (this.recipe instanceof BlastRecipe) { - new RecipeEditorBlast((BlastRecipe) this.recipe, categoryData, null, ButtonType.ChooseBlastType,false).menuOpen(player); + new RecipeEditorBlast((BlastRecipe) this.recipe, this.page,categoryData, null, ButtonType.ChooseBlastType, false).menuOpen(player); } if (this.recipe instanceof SmokerRecipe) { - new RecipeEditorSmoker((SmokerRecipe) this.recipe, categoryData, null, ButtonType.ChooseSmokerType,false).menuOpen(player); + new RecipeEditorSmoker((SmokerRecipe) this.recipe, this.page,categoryData, null, ButtonType.ChooseSmokerType, false).menuOpen(player); } } if (value.isActionTypeEqual(ButtonType.Back.name())) { - if (this.categoryData != null) - new RecipesViewer(this.categoryData, "", player).menuOpen(player); - else + if (this.categoryData != null) { + final RecipesViewer recipesViewer = new RecipesViewer(this.categoryData, "", player); + recipesViewer.menuOpen(player); + if ( this.page > 0) + recipesViewer.setPage( this.page); + } else new EditorTypeSelector(null, permission).menuOpen(player); } return onPlayerClick(this.recipe, this.categoryData, this.permission, value.getActionType(), player); @@ -261,9 +270,9 @@ protected boolean onPlayerClick(final RecipeT recipe, final CategoryData categor @Nullable private ItemStack[] getIngredients(final Map map, final Player player) { - - final int resultSlot = this.menuTemplate.getFillSlots().get(recipe.getContent().length); - final List arrays = new ArrayList<>(recipe.getContent().length); + List fillSlots = this.menuTemplate.getFillSlots(); + final int resultSlot = fillSlots != null && fillSlots.size() > recipe.getContent().length ? this.menuTemplate.getFillSlots().get(recipe.getContent().length) : fillSlots.size(); + final List stackList = new ArrayList<>(recipe.getContent().length); int index = 0; for (final Integer slot : this.menuTemplate.getFillSlots()) { final ItemStack itemStack = map.get(slot); @@ -271,8 +280,10 @@ private ItemStack[] getIngredients(final Map map, final Play Messenger.Message("Recipes only support amounts of 1 in the content.", player); itemStack.setAmount(1); } + if(index > stackList.size()) + break; if (slot != resultSlot) - arrays.add(index, itemStack); + stackList.add(index, itemStack); if (slot == resultSlot) this.recipe.setResultSlot(index); index++; @@ -280,12 +291,12 @@ private ItemStack[] getIngredients(final Map map, final Play } this.result = map.remove(resultSlot); - if (!arrays.stream().anyMatch(x -> x != null)) { + if (!stackList.stream().anyMatch(x -> x != null)) { return null; } if (recipe instanceof FurnaceRecipe) - return arrays.toArray(new ItemStack[1]); - final ItemStack[] itemstacks = arrays.toArray(new ItemStack[0]); + return stackList.toArray(new ItemStack[1]); + final ItemStack[] itemstacks = stackList.toArray(new ItemStack[0]); /* for (final ItemStack lastItem : itemstacks){ if (lastItem != null) lastItemIndex++; @@ -302,42 +313,6 @@ private ItemStack[] getIngredients(final Map map, final Play return itemstacks; } - public boolean handlePositionChange(final String message) { - if (message == null || message.trim() == "") return false; - - if (message.equals("") || message.equalsIgnoreCase("q") || message.equalsIgnoreCase("cancel") || message.equalsIgnoreCase("quit") || message.equalsIgnoreCase("exit")) - return false; - - final String[] args = message.split(" "); - - if (args.length != 2) { - Messenger.Message("Please specify a page and slot number separated by a space.", getViewer()); - return true; - } - int page = 0, slot = 0; - try { - page = Integer.parseInt(args[0]); - } catch (final NumberFormatException e) { - Messenger.Message("Could not parse the page number.", getViewer()); - return true; - } - - try { - slot = Integer.parseInt(args[1]); - } catch (final NumberFormatException e) { - Messenger.Message("Could not parse the slot number.", getViewer()); - return true; - } - recipe.setPage(page); - recipe.setSlot(slot); - - Messenger.Message("Set the page to " + page + ", and the slot to " + slot + ". This will get auto-filled if it's not available.", getViewer()); - self().getFm().saveRecipe(recipe); - - //updatePlaceHolders(); - return false; - } - private Map getPlaceholders() { final Map placeHolders = new HashMap() {{ put(InfoItemPlaceHolders.Key.getPlaceHolder(), recipe.getKey() == null ? "null" : recipe.getKey()); diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorBlast.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorBlast.java index 8b27efc..df27e9f 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorBlast.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorBlast.java @@ -12,8 +12,8 @@ public class RecipeEditorBlast extends RecipeEditor { - public RecipeEditorBlast(final BlastRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType,boolean clearItems) { - super(recipe, categoryData, permission, editorType,clearItems); + public RecipeEditorBlast(final BlastRecipe recipe,final int page,final CategoryData categoryData, final String permission, final ButtonType editorType, boolean clearItems) { + super(recipe, page, categoryData, permission, editorType, clearItems); } diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorFurnace.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorFurnace.java index 4b6e30d..aaa439f 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorFurnace.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorFurnace.java @@ -13,12 +13,12 @@ public class RecipeEditorFurnace extends RecipeEditor { - public RecipeEditorFurnace(final FurnaceRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { - super(recipe, categoryData, permission, editorType, true); + public RecipeEditorFurnace(final FurnaceRecipe recipe,final int page, final CategoryData categoryData, final String permission, final ButtonType editorType) { + super(recipe, page,categoryData, permission, editorType, true); } - public RecipeEditorFurnace(final FurnaceRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType,boolean clearItems) { - super(recipe, categoryData, permission, editorType, clearItems); + public RecipeEditorFurnace(final FurnaceRecipe recipe,final int page, final CategoryData categoryData, final String permission, final ButtonType editorType,boolean clearItems) { + super(recipe, page,categoryData, permission, editorType, clearItems); } @Override diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorSmoker.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorSmoker.java index 25f7eb2..7ed7374 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorSmoker.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/editors/RecipeEditorSmoker.java @@ -12,8 +12,8 @@ public class RecipeEditorSmoker extends RecipeEditor { - public RecipeEditorSmoker(final SmokerRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType,boolean clearItems) { - super(recipe, categoryData, permission, editorType, clearItems); + public RecipeEditorSmoker(final SmokerRecipe recipe,final int page, final CategoryData categoryData, final String permission, final ButtonType editorType,boolean clearItems) { + super(recipe, page,categoryData, permission, editorType, clearItems); } @Override diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettings.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettings.java index 2371160..1c7f728 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettings.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettings.java @@ -45,6 +45,7 @@ public class RecipeSettings extends MenuHolder { private final MenuSettingsCache menuSettingsCache = self().getMenuSettingsCache(); + protected final int page; private String permission; private final MenuTemplate menuTemplate; @Getter @@ -53,7 +54,8 @@ public class RecipeSettings extends MenuHolder { private final ButtonType editorType; private final CategoryData categoryData; - public RecipeSettings(final RecipeT recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { + public RecipeSettings(final RecipeT recipe, int pageNumber, final CategoryData categoryData, final String permission, final ButtonType editorType) { + page = pageNumber; if (permission == null || permission.equals("")) this.permission = recipe.getPermission(); else this.permission = permission; this.editorType = editorType; @@ -72,7 +74,7 @@ public RecipeSettings(final RecipeT recipe, final CategoryData categoryData, fin if (menuTemplate != null) { setMenuSize(GuiUtil.invSize("RecipeSettings", this.menuTemplate.getAmountOfButtons())); final String title = menuTemplate.getMenuTitle() == null ? "editor" : menuTemplate.getMenuTitle().replace(InfoItemPlaceHolders.Recipe_type.getPlaceHolder(), recipe.getType().name().toLowerCase()); - setTitle(()-> title); + setTitle(() -> title); //setFillSpace(menuTemplate.getFillSlots()); setMenuOpenSound(this.menuTemplate.getSound()); } @@ -121,12 +123,12 @@ public ItemStack getItem() { public boolean run(final MenuButtonData value, final Inventory menu, final Player player, final ClickType click) { if (value.isActionTypeEqual(ButtonType.SetPosition.name())) { - new HandleChatInput(this, this::handlePositionChange); + new HandleChatInput(this, this::handlePositionChange).setMessages("Type specify a page and slot number separated by a space or type q, exit or cancel to close conversion.").start(player); return true; } if (value.isActionTypeEqual(ButtonType.AllowedWorldsCraft.name())) { if (player.isConversing()) return true; - if (click.isRightClick() && click.isShiftClick()){ + if (click.isRightClick() && click.isShiftClick()) { recipe.getAllowedWorlds().clear(); return true; } @@ -194,9 +196,9 @@ public boolean run(final MenuButtonData value, final Inventory menu, final Playe } if (value.isActionTypeEqual(ButtonType.Back.name())) { if (this.recipe instanceof WBRecipe) { - new RecipeEditor<>((WBRecipe) this.recipe, categoryData, null, ButtonType.ChooseWorkbenchType,false).menuOpen(player); + new RecipeEditor<>((WBRecipe) this.recipe, this.page, this.categoryData, null, ButtonType.ChooseWorkbenchType, false).menuOpen(player); } - handleBack(this.recipe,categoryData, player); + handleBack(this.recipe, categoryData, player); } return onPlayerClick(this.recipe, value.getActionType(), player); } @@ -268,8 +270,8 @@ public boolean handlePositionChange(final String message) { } recipe.setPage(page); recipe.setSlot(slot); - Messenger.Message("Set the page to " + page + ", and the slot to " + slot + ". This will get auto-filled if it's not available.", getViewer()); + this.runTask(() -> this.menuOpen(player)); return false; } @@ -373,6 +375,7 @@ protected void runTask(final Runnable runnable) { protected boolean onPlayerClick(final RecipeT recipe, final String buttonAction, final Player player) { return false; } + protected void handleBack(final RecipeT recipe, final CategoryData categoryData, final Player player) { } diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsBlast.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsBlast.java index b0a8c3c..6cc5578 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsBlast.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsBlast.java @@ -15,7 +15,7 @@ public class RecipeSettingsBlast extends RecipeSettings{ public RecipeSettingsBlast(final BlastRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { - super(recipe, categoryData, permission, editorType); + super(recipe, 0,categoryData, permission, editorType); } protected boolean onPlayerClick(final BlastRecipe blastRecipe, final String buttonAction, final Player player) { @@ -73,7 +73,7 @@ protected boolean onPlayerClick(final BlastRecipe blastRecipe, final String butt @Override protected void handleBack(final BlastRecipe recipe, final CategoryData categoryData, final Player player) { - new RecipeEditorBlast(recipe, categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); + new RecipeEditorBlast(recipe, this.page,categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); } @Override diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsFurnace.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsFurnace.java index d52b0de..e93c570 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsFurnace.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsFurnace.java @@ -15,7 +15,7 @@ public class RecipeSettingsFurnace extends RecipeSettings { public RecipeSettingsFurnace(final FurnaceRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { - super(recipe, categoryData, permission, editorType); + super(recipe, 0, categoryData, permission, editorType); } protected boolean onPlayerClick(final FurnaceRecipe furnaceRecipe, final String buttonAction, final Player player) { @@ -44,7 +44,7 @@ protected boolean onPlayerClick(final FurnaceRecipe furnaceRecipe, final String }).setMessages("Please input an exp amount.Type q, exit, cancel to turn it off.").start(getViewer()); return true; } - if (buttonAction.equalsIgnoreCase( ButtonType.SetCookTime.name())) { + if (buttonAction.equalsIgnoreCase(ButtonType.SetCookTime.name())) { if (player.isConversing()) return true; new HandleChatInput(this, (msg) -> { short parsed; @@ -73,7 +73,7 @@ protected boolean onPlayerClick(final FurnaceRecipe furnaceRecipe, final String @Override protected void handleBack(final FurnaceRecipe recipe, final CategoryData categoryData, final Player player) { - new RecipeEditorFurnace(recipe, categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); + new RecipeEditorFurnace(recipe, this.page, categoryData, null, ButtonType.ChooseFurnaceType, false).menuOpen(player); } @Override diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsSmoker.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsSmoker.java index 0b5832d..d0e3e5d 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsSmoker.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/settings/RecipeSettingsSmoker.java @@ -15,7 +15,7 @@ public class RecipeSettingsSmoker extends RecipeSettings{ public RecipeSettingsSmoker(final SmokerRecipe recipe, final CategoryData categoryData, final String permission, final ButtonType editorType) { - super(recipe, categoryData, permission, editorType); + super(recipe, 0,categoryData, permission, editorType); } protected boolean onPlayerClick(final SmokerRecipe furnaceRecipe, final String buttonAction, final Player player) { if (player.isConversing()) return true; @@ -72,7 +72,7 @@ protected boolean onPlayerClick(final SmokerRecipe furnaceRecipe, final String b @Override protected void handleBack(final SmokerRecipe recipe, final CategoryData categoryData, final Player player) { - new RecipeEditorSmoker(recipe, categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); + new RecipeEditorSmoker(recipe, this.page,categoryData, null, ButtonType.ChooseFurnaceType,false).menuOpen(player); } @Override diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/guis/viewers/RecipeViewRecipe.java b/src/main/java/com/dutchjelly/craftenhance/gui/guis/viewers/RecipeViewRecipe.java index 53f8634..fbdf958 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/guis/viewers/RecipeViewRecipe.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/guis/viewers/RecipeViewRecipe.java @@ -42,8 +42,9 @@ public class RecipeViewRecipe extends MenuHolder private final MenuTemplate menuTemplate; private final CategoryData categoryData; private final RecipeT recipe; + private final int page; - public RecipeViewRecipe(final CategoryData categoryData, final RecipeT recipe, final String menuType) { + public RecipeViewRecipe(final CategoryData categoryData, final int pageNumber, final RecipeT recipe, final String menuType) { super(formatRecipes(recipe, null, false)); this.recipe = recipe; this.categoryData = categoryData; @@ -53,6 +54,7 @@ public RecipeViewRecipe(final CategoryData categoryData, final RecipeT recipe, f setMenuSize(27); this.setUseColorConversion(true); this.setIgnoreItemCheck(true); + this.page = pageNumber; } @Override @@ -109,21 +111,24 @@ public ItemStack getItem() { public boolean run(final MenuButtonData value, final Inventory menu, final Player player, final ClickType click) { if (value.isActionTypeEqual(ButtonType.Back.name())) { - new RecipesViewer(categoryData, "", player).menuOpen(player); + final RecipesViewer recipesViewer = new RecipesViewer(this.categoryData, "", player); + recipesViewer.menuOpen(player); + if ( this.page > 0) + recipesViewer.setPage( this.page); } if (value.isActionTypeEqual(ButtonType.edit_recipe.name())) { if (player.hasPermission(PermissionTypes.Edit.getPerm())) { if (recipe instanceof WBRecipe) { - new RecipeEditor<>((WBRecipe) recipe, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); + new RecipeEditor<>((WBRecipe) recipe, this.page, categoryData, null, ButtonType.ChooseWorkbenchType).menuOpen(player); } if (recipe instanceof FurnaceRecipe) { - new RecipeEditorFurnace((FurnaceRecipe) recipe, categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); + new RecipeEditorFurnace((FurnaceRecipe) recipe, this.page,categoryData, null, ButtonType.ChooseFurnaceType, true).menuOpen(player); } if (recipe instanceof BlastRecipe) { - new RecipeEditorBlast((BlastRecipe) recipe, categoryData, null, ButtonType.ChooseBlastType, true).menuOpen(player); + new RecipeEditorBlast((BlastRecipe) recipe, this.page,categoryData, null, ButtonType.ChooseBlastType, true).menuOpen(player); } if (recipe instanceof SmokerRecipe) { - new RecipeEditorSmoker((SmokerRecipe) recipe, categoryData, null, ButtonType.ChooseSmokerType, true).menuOpen(player); + new RecipeEditorSmoker((SmokerRecipe) recipe, this.page,categoryData, null, ButtonType.ChooseSmokerType, true).menuOpen(player); } } return false; diff --git a/src/main/java/com/dutchjelly/craftenhance/gui/util/GuiUtil.java b/src/main/java/com/dutchjelly/craftenhance/gui/util/GuiUtil.java index cecdfdb..9054bd9 100644 --- a/src/main/java/com/dutchjelly/craftenhance/gui/util/GuiUtil.java +++ b/src/main/java/com/dutchjelly/craftenhance/gui/util/GuiUtil.java @@ -183,7 +183,7 @@ public static int invSize(final String menu, final int size) { if (size <= 36) return 36; if (size <= 45) return 45; if (size > 54) - Messenger.Error("This menu "+ menu + " has set bigger inventory size an it can handle, your set size " + size + ". will defult to 54."); + Messenger.Error("This menu "+ menu + " has set bigger inventory size an it can handle, your set size " + size + ". will default to 54."); return 54; } diff --git a/src/main/java/com/dutchjelly/craftenhance/messaging/Debug.java b/src/main/java/com/dutchjelly/craftenhance/messaging/Debug.java index 52cf656..8e26f7d 100644 --- a/src/main/java/com/dutchjelly/craftenhance/messaging/Debug.java +++ b/src/main/java/com/dutchjelly/craftenhance/messaging/Debug.java @@ -3,6 +3,7 @@ import com.dutchjelly.craftenhance.CraftEnhance; import java.util.function.Supplier; +import java.util.logging.Level; import java.util.logging.Logger; import static com.dutchjelly.craftenhance.messaging.Debug.Type.Crafting; @@ -59,13 +60,21 @@ public static void Send(final Object sender, final Object obj) { public static void Send(final Object[] arr) { if (arr == null) return; logger.info(prefix + " "); - for (int i = 0; i < arr.length; i++) { - if (arr[i] == null) continue; - logger.info(arr[i].toString()); + for (final Object o : arr) { + if (o == null) continue; + logger.info(o.toString()); } logger.info(""); } + public static void error(final String message) { + logger.log(Level.WARNING,prefix + message); + } + public static void error(final String message,Throwable throwable) { + logger.log(Level.WARNING,prefix + message,throwable); + } + + public enum Type { Crafting, Smelting, diff --git a/src/main/java/com/dutchjelly/craftenhance/prompt/HandleChatInput.java b/src/main/java/com/dutchjelly/craftenhance/prompt/HandleChatInput.java index d6dc50b..1a58e47 100644 --- a/src/main/java/com/dutchjelly/craftenhance/prompt/HandleChatInput.java +++ b/src/main/java/com/dutchjelly/craftenhance/prompt/HandleChatInput.java @@ -52,7 +52,7 @@ protected void onConversationEnd(final SimpleConversation conversation, final Co } @Override - protected ConversationCanceller getCanceller() { + protected ConversationCanceller getCanceller() { return new SimpleCanceller(" "); } @@ -77,6 +77,8 @@ protected String getPrompt(final ConversationContext conversationContext) { Player player = getPlayer(); if (conversationContext.getForWhom() instanceof Player) player = getPlayer(conversationContext); + if (this.messages.isEmpty()) return "No message set for this conversion: " + this.menuHolder.getTitle(); + final String lastMessage = this.messages.get(this.messages.size() - 1); for (final String message : this.messages) { if (message.equals(lastMessage)) break; diff --git a/src/main/java/com/dutchjelly/craftenhance/util/PaginatedItems.java b/src/main/java/com/dutchjelly/craftenhance/util/PaginatedItems.java new file mode 100644 index 0000000..c28ad36 --- /dev/null +++ b/src/main/java/com/dutchjelly/craftenhance/util/PaginatedItems.java @@ -0,0 +1,123 @@ +package com.dutchjelly.craftenhance.util; + +import com.dutchjelly.craftenhance.crafthandling.recipes.EnhancedRecipe; +import com.dutchjelly.craftenhance.files.CategoryData; +import org.broken.arrow.menu.button.manager.library.utility.MenuTemplate; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.dutchjelly.craftenhance.gui.util.FormatListContents.canSeeRecipes; + +public class PaginatedItems { + + private final CategoryData categoryData; + private final int slotsPerPage; + private final List itemList = new ArrayList<>(); + private final List needSortItems = new ArrayList<>(); + + public PaginatedItems(final CategoryData categoryData, final MenuTemplate menuTemplate) { + this.categoryData = categoryData; + this.slotsPerPage = menuTemplate.getFillSlots() != null ? menuTemplate.getFillSlots().size() : 0; + } + + public List retrieveList(final Player player, final String recipeSearchFor) { + List enhancedRecipes = canSeeRecipes(categoryData.getEnhancedRecipes(recipeSearchFor), player); + for (EnhancedRecipe recipe : enhancedRecipes) { + addItem(new Item(recipe)); + } + for (Item item : needSortItems) { + addDuplicates(item); + } + return itemList.stream() + .map(item -> (item != null) ? item.getEnhancedRecipe() : null) + .collect(Collectors.toList()); + } + + private void addDuplicates(final Item item) { + int index = (item.page != -1) ? findNextFreeSlotInPage(item.page) : findNextFreeSlot(); + if (index == -1) { + // No space left, move to a new page + item.page = (itemList.size() / slotsPerPage) + 1; + item.slot = 0; + index = getIndex(item.page, item.slot); + } + ensureCapacity(index); + itemList.set(index, item); + } + + private void addItem(Item item) { + if (item.page != -1 && item.slot != -1) { + // Attempt to place fixed slot item + int index = getIndex(item.page, item.slot); + ensureCapacity(index); + if (itemList.get(index) == null) { + itemList.set(index, item); + return; + } + item.slot = -1; + } + needSortItems.add(item); + } + + + private int getIndex(int page, int slot) { + return (page - 1) * slotsPerPage + slot; + } + + private void ensureCapacity(int index) { + while (itemList.size() <= index) { + itemList.add(null); + } + } + + private int findNextFreeSlotInPage(int page) { + int start = getIndex(page, 0); + int end = start + slotsPerPage; + for (int i = start; i < end; i++) { + if (i >= itemList.size() || itemList.get(i) == null) { + return i; + } + } + return -1; + } + + private int findNextFreeSlot() { + int index = 0; + for (int i = 0; i < itemList.size(); i++) { + index++; + if (itemList.get(i) == null) { + return i; + } + } + return index >= itemList.size() - 1 ? itemList.size() : index; + } + + public static class Item { + private final EnhancedRecipe enhancedRecipe; + int page; + int slot; + + Item(final EnhancedRecipe recipe) { + this.enhancedRecipe = recipe; + this.page = recipe.getPage(); + this.slot = recipe.getSlot() > 0 ? recipe.getSlot() - 1 : recipe.getSlot(); + //this.slot = recipe.getSlot(); + } + + public EnhancedRecipe getEnhancedRecipe() { + return enhancedRecipe; + } + + @Override + public String toString() { + return "Item{" + + "enhancedRecipe=" + enhancedRecipe + + ", page=" + page + + ", slot=" + slot + + '}'; + } + } +}