From 41c43b792a67d3181ed627e838b008798fe193b4 Mon Sep 17 00:00:00 2001 From: youyihj <30400991+friendlyhj@users.noreply.github.com> Date: Sun, 7 Feb 2021 12:28:46 +0800 Subject: [PATCH] Refactor replaceAllOccurences & add progress bar when applying actions (#1144) * refactor replaceAllOccurences * remove timer * don't replace shapeless recipes' ingredient to null * add domain to modified recipe name * add progress bar while apply actions * fix * fix * fix * clear after replace all occurences action is applied. * upper case * add IIngredientPredicate to determine toReplace * remove unused field * A or B -> C or B * a little change * clear comment * replace with null in shapeless recipes -> remove the ingredient * describe sub actions * log exception * override IBlockEvent#getBlock in changing block events * add break in checkShapelessNulls * code style change * wrong method name * move class * if there is a `recipes.remove` wants to remove origin recipe, don't replace the recipe --- .../crafttweaker/mc1120/CraftTweaker.java | 59 +++- .../events/handling/MCBlockBreakEvent.java | 6 + .../handling/MCBlockHarvestDropsEvent.java | 6 + .../events/handling/MCBlockPlaceEvent.java | 6 + .../mc1120/recipes/MCRecipeManager.java | 328 ++++++++++-------- 5 files changed, 237 insertions(+), 168 deletions(-) diff --git a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/CraftTweaker.java b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/CraftTweaker.java index 7bd8fff4f..2e86a2ac1 100644 --- a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/CraftTweaker.java +++ b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/CraftTweaker.java @@ -3,6 +3,7 @@ import crafttweaker.*; import crafttweaker.annotations.*; import crafttweaker.api.network.NetworkSide; +import crafttweaker.api.recipes.ICraftingRecipe; import crafttweaker.mc1120.brewing.MCBrewing; import crafttweaker.mc1120.client.MCClient; import crafttweaker.mc1120.commands.CTChatCommand; @@ -154,22 +155,34 @@ public void onPreInitialization(FMLPreInitializationEvent ev) { @EventHandler public void onPostInit(FMLPostInitializationEvent ev) { MinecraftForge.EVENT_BUS.post(new ActionApplyEvent.Pre()); - try { - MCRecipeManager.recipes = ForgeRegistries.RECIPES.getEntries(); - CraftTweakerAPI.apply(MCRecipeManager.actionRemoveRecipesNoIngredients); - MCRecipeManager.recipesToRemove.forEach(CraftTweakerAPI::apply); - MCRecipeManager.recipesToAdd.forEach(CraftTweakerAPI::apply); - MCFurnaceManager.recipesToRemove.forEach(CraftTweakerAPI::apply); - MCFurnaceManager.recipesToAdd.forEach(CraftTweakerAPI::apply); - LATE_ACTIONS.forEach(CraftTweakerAPI::apply); - MCRecipeManager.recipes = ForgeRegistries.RECIPES.getEntries(); - - //Cleanup - MCRecipeManager.cleanUpRecipeList(); - } catch(Exception e) { - CraftTweaker.LOG.catching(e); - CraftTweakerAPI.logError("Error while applying actions", e); + MCRecipeManager.recipes = ForgeRegistries.RECIPES.getEntries(); + if (MCRecipeManager.ActionReplaceAllOccurences.INSTANCE.hasSubAction()) { + MCRecipeManager.ActionReplaceAllOccurences.INSTANCE.describeSubActions(); + List recipes = CraftTweakerAPI.recipes.getAll(); + ProgressManager.ProgressBar progressBar = ProgressManager.push("Applying replace all occurences action", recipes.size()); + for (ICraftingRecipe recipe : recipes) { + try { + progressBar.step(recipe.getFullResourceName()); + MCRecipeManager.ActionReplaceAllOccurences.INSTANCE.setCurrentModifiedRecipe(recipe); + MCRecipeManager.ActionReplaceAllOccurences.INSTANCE.apply(); + } catch (Exception e) { + CraftTweaker.LOG.catching(e); + CraftTweakerAPI.logError("Fail to apply replace all occurence action at recipe " + recipe.getFullResourceName(), e); + } + } + ProgressManager.pop(progressBar); } + applyActions(Collections.singletonList(MCRecipeManager.actionRemoveRecipesNoIngredients), "applying action remove recipes without ingredients", "fail to apply recipes without ingredient"); + applyActions(MCRecipeManager.recipesToRemove, "Applying remove recipe actions", "Fail to apply remove recipe actions"); + applyActions(MCRecipeManager.recipesToAdd, "Applying add recipe actions", "Fail to apply add recipe actions"); + applyActions(MCFurnaceManager.recipesToRemove, "Applying remove furnace recipe actions", "Fail to apply remove furnace recipe actions"); + applyActions(MCFurnaceManager.recipesToAdd, "Applying add furnace recipe actions", "Fail to apply add furnace recipe actions"); + applyActions(LATE_ACTIONS, "Applying late actions", "Fail to apply late actions"); + MCRecipeManager.recipes = ForgeRegistries.RECIPES.getEntries(); + MCRecipeManager.ActionReplaceAllOccurences.INSTANCE.clear(); + + //Cleanup + MCRecipeManager.cleanUpRecipeList(); MinecraftForge.EVENT_BUS.post(new ActionApplyEvent.Post()); } @@ -228,4 +241,20 @@ public void onServerStopped(FMLServerStoppedEvent ev) { CrafttweakerImplementationAPI.setScriptProvider(scriptsGlobal); server = null; } + + private void applyActions(List actions, String applyingMessage, String errorMessage) { + if (!actions.isEmpty()) { + ProgressManager.ProgressBar progressBar = ProgressManager.push(applyingMessage, actions.size()); + actions.forEach(action -> { + progressBar.step(action.describe()); + try { + CraftTweakerAPI.apply(action); + } catch (Exception e) { + CraftTweaker.LOG.catching(e); + CraftTweakerAPI.logError(errorMessage + " at action " + action.describe(), e); + } + }); + ProgressManager.pop(progressBar); + } + } } diff --git a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockBreakEvent.java b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockBreakEvent.java index 1ff925d86..9e2f15e86 100644 --- a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockBreakEvent.java +++ b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockBreakEvent.java @@ -1,5 +1,6 @@ package crafttweaker.mc1120.events.handling; +import crafttweaker.api.block.IBlock; import crafttweaker.api.event.BlockBreakEvent; import crafttweaker.api.minecraft.CraftTweakerMC; import crafttweaker.api.player.IPlayer; @@ -26,6 +27,11 @@ public IPlayer getPlayer() { return CraftTweakerMC.getIPlayer(event.getPlayer()); } + @Override + public IBlock getBlock() { + return getBlockState().getBlock(); + } + @Override public int getExperience() { return event.getExpToDrop(); diff --git a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockHarvestDropsEvent.java b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockHarvestDropsEvent.java index 5cd135776..545c66320 100644 --- a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockHarvestDropsEvent.java +++ b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockHarvestDropsEvent.java @@ -1,5 +1,6 @@ package crafttweaker.mc1120.events.handling; +import crafttweaker.api.block.IBlock; import crafttweaker.api.event.BlockHarvestDropsEvent; import crafttweaker.api.item.WeightedItemStack; import crafttweaker.api.minecraft.CraftTweakerMC; @@ -34,6 +35,11 @@ public int getFortuneLevel() { return event.getFortuneLevel(); } + @Override + public IBlock getBlock() { + return getBlockState().getBlock(); + } + @Override public List getDrops() { return CraftTweakerMC.getWeightedItemStackList(event.getDrops()); diff --git a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockPlaceEvent.java b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockPlaceEvent.java index 4916d6d0c..ef512b93b 100644 --- a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockPlaceEvent.java +++ b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/events/handling/MCBlockPlaceEvent.java @@ -1,5 +1,6 @@ package crafttweaker.mc1120.events.handling; +import crafttweaker.api.block.IBlock; import crafttweaker.api.block.IBlockState; import crafttweaker.api.event.BlockPlaceEvent; import crafttweaker.api.minecraft.CraftTweakerMC; @@ -29,6 +30,11 @@ public IBlockState getPlacedAgainst() { return CraftTweakerMC.getBlockState(event.getPlacedAgainst()); } + @Override + public IBlock getBlock() { + return getBlockState().getBlock(); + } + @Override public String getHand() { return String.valueOf(event.getHand()); diff --git a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/recipes/MCRecipeManager.java b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/recipes/MCRecipeManager.java index 65d11e200..a378d7a10 100644 --- a/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/recipes/MCRecipeManager.java +++ b/CraftTweaker2-MC1120-Main/src/main/java/crafttweaker/mc1120/recipes/MCRecipeManager.java @@ -4,7 +4,7 @@ import crafttweaker.IAction; import crafttweaker.api.item.IIngredient; import crafttweaker.api.item.IItemStack; -import crafttweaker.api.item.IngredientAny; +import crafttweaker.api.item.IngredientOr; import crafttweaker.api.minecraft.CraftTweakerMC; import crafttweaker.api.recipes.*; import crafttweaker.mc1120.item.MCItemStack; @@ -22,13 +22,13 @@ import net.minecraftforge.oredict.ShapelessOreRecipe; import net.minecraftforge.registries.GameData; import net.minecraftforge.registries.RegistryManager; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import stanhebben.zenscript.annotations.Optional; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static crafttweaker.api.minecraft.CraftTweakerMC.getIItemStack; import static crafttweaker.api.minecraft.CraftTweakerMC.getItemStack; @@ -190,8 +190,9 @@ public void addHiddenShapeless(String name, IItemStack output, IIngredient[] ing private boolean checkShapelessNulls(IItemStack output, IIngredient[] ingredients) { boolean valid = output != null; for(IIngredient ing : ingredients) { - if(ing == null) { + if (ing == null) { valid = false; + break; } } if(!valid) { @@ -288,7 +289,9 @@ public IItemStack craft(IItemStack[][] contents) { @Override public void replaceAllOccurences(IIngredient toReplace, IIngredient replaceWith, IIngredient forOutput) { - recipesToRemove.add(new ActionReplaceAllOccurences(toReplace, replaceWith, forOutput)); + ActionReplaceAllOccurences.INSTANCE.addSubAction( + new SubActionReplaceAllOccurences(toReplace, replaceWith, forOutput) + ); } @@ -302,134 +305,6 @@ public void removeRecipes(List removingRecipes) { } } - public static class ActionReplaceAllOccurences extends ActionBaseRemoveRecipes { - - //I'm odd, in that I'm an ActionBaseRemoveRecipes, that also creates recipes. - private final IIngredient toReplace; - private final IIngredient replaceWith; - private final IIngredient forOutput; - private List toChange; - private List toRemove; - - public ActionReplaceAllOccurences(IIngredient toReplace, IIngredient replaceWith, IIngredient forOutput) { - this.toReplace = toReplace; - this.replaceWith = replaceWith; - this.forOutput = forOutput == null ? IngredientAny.INSTANCE : forOutput; - } - - @Override - public void apply() { - toChange = getAllForIngredient(toReplace); - toRemove = toChange.stream() - .map(f -> new ResourceLocation(f.getFullResourceName())) - .collect(Collectors.toList()); - removeRecipes(toRemove); - changeIngredients(toChange); - } - - @Override - public void removeRecipes(List removingRecipes) { - List toUnAdd = new ArrayList<>(); - removingRecipes.forEach(recipe -> { - RegistryManager.ACTIVE.getRegistry(GameData.RECIPES).remove(recipe); - recipesToAdd.stream() - .filter(f -> f instanceof ActionDummyAddRecipe) - .filter(f -> f.recipe.getRegistryName().equals(recipe)) - .forEach(toUnAdd::add); - }); - toUnAdd.forEach(f -> { - recipesToAdd.remove(f); - usedRecipeNames.remove(f.getName()); - }); - } - - @Override - public String describe() { - return "Removing all occurences of ingredient: " + toReplace + " and replacing them with " + replaceWith; - } - - private void changeIngredients(List toChange) { - for(MCRecipeBase recipe : toChange) { - if(recipe.isShaped()) { - IIngredient[][] ingredients = recipe.getIngredients2D(); - for(IIngredient[] targRow : ingredients) { - for(int i = 0; i < targRow.length; i++) { - if(targRow[i] != null && targRow[i].contains(toReplace)) - targRow[i] = replaceWith; - } - } - - MCRecipeShaped newRecipe = new MCRecipeShaped(ingredients, recipe.getOutput(), recipe.recipeFunction, recipe.getRecipeAction(), false, recipe.hidden); - registerNewRecipe(newRecipe, getNewRecipeName(recipe)); - - } else { - if(replaceWith == null) - continue; //No null's in shapeless recipies... We can't do anything, so we just won't add the recipe. - IIngredient[] ingredients = recipe.getIngredients1D(); - for(int i = 0; i < ingredients.length; i++) { - final IIngredient ingredient = ingredients[i]; - if(ingredient != null && ingredient.contains(toReplace)) { - ingredients[i] = replaceWith; - } - } - MCRecipeShapeless newRecipe = new MCRecipeShapeless(ingredients, recipe.output, recipe.recipeFunction, recipe.recipeAction, recipe.hidden); - registerNewRecipe(newRecipe, getNewRecipeName(recipe)); - } - } - - } - - private void registerNewRecipe(MCRecipeBase newRecipe, String name) { - ActionDummyAddRecipe dummyRecipe = new ActionDummyAddRecipe(newRecipe, newRecipe.output, true); - dummyRecipe.setName(name); - MCRecipeManager.recipesToAdd.add(dummyRecipe); - ForgeRegistries.RECIPES.register(newRecipe); - } - - private String getNewRecipeName(MCRecipeBase recipe) { - if(recipe.getName().contains("modified")) { - //This should keep adding re in front of a modified recipe every time it's modified. - //If you wind up with rererereremodified recipes, you're doing something wrong. Stop it. - return recipe.getName().replace("modified", "remodified"); - } - return recipe.getRegistryName().getResourceDomain() + "-" + recipe.getName() + "-modified"; - } - - private List getAllForIngredient(IIngredient target) { - Set> recipes; - List results = new ArrayList<>(); - - recipes = ForgeRegistries.RECIPES.getEntries(); - - for(Map.Entry recipeEntry : recipes) { - IRecipe recipe = recipeEntry.getValue(); - - //Check the recipe output if provided. - { - final IItemStack output = CraftTweakerMC.getIItemStack(recipe.getRecipeOutput()); - if(forOutput != IngredientAny.INSTANCE && output != null && !forOutput.matches(output)) - continue; - } - - for(Ingredient ingredient : recipe.getIngredients()) { - final IIngredient iIngredient = CraftTweakerMC.getIIngredient(ingredient); - if(iIngredient == null) - continue; - if(target.contains(iIngredient)) { - if(recipe instanceof MCRecipeBase) { - results.add((MCRecipeBase) recipe); - break; //One ingredient is enough to get it added, bail out of ingredient loop. - } else { - results.add(new MCRecipeWrapper(recipe)); - break; //See previous comment. - } - } - } - } - return results; - } - } - public static class ActionRemoveShapedRecipes extends ActionBaseRemoveRecipes { IIngredient output; @@ -852,27 +727,6 @@ public MCRecipeBase getRecipe() { } } - public static class ActionDummyAddRecipe extends ActionBaseAddRecipe { - - public ActionDummyAddRecipe(MCRecipeBase recipe, IItemStack output, boolean isShaped) { - super(recipe, output, isShaped); - } - - //This whole class is a dirty hack. - //It exists only to hold information for the CraftTweaker JEI plugin. - @Override - public void apply() { - //Our work was done elsehwere, apply is a noop. - } - - @Override - protected void setName(String name) { - super.setName(name); - //This is necessary to allow recipe name progression if we repeatedly modify a recipe. - recipe.setRegistryName(new ResourceLocation("crafttweaker", this.name)); - } - } - private static class ActionAddShapedRecipe extends ActionBaseAddRecipe { public ActionAddShapedRecipe(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function, IRecipeAction action, boolean mirrored, boolean hidden) { @@ -896,6 +750,174 @@ public ActionAddShapelessRecipe(String name, IItemStack output, IIngredient[] in setName(name); } } + + public static class SubActionReplaceAllOccurences implements IAction { + private SubActionReplaceAllOccurences next; + private final IIngredient toReplace; + private final IIngredient replaceWith; + private final IIngredient forOutput; + + public SubActionReplaceAllOccurences(IIngredient toReplace, IIngredient replaceWith, IIngredient forOutput) { + this.toReplace = toReplace; + this.replaceWith = replaceWith; + this.forOutput = forOutput; + } + + @Override + public void apply() { + if (ActionReplaceAllOccurences.INSTANCE.currentModifiedRecipe == null) { + return; + } + if (forOutput == null || forOutput.contains(ActionReplaceAllOccurences.INSTANCE.currentModifiedRecipe.getOutput())) { + applyShapedPattern(); + applyShapelessPattern(); + } + } + + @Override + public String describe() { + String replaceWithName = replaceWith == null ? MCItemStack.EMPTY.toString() : replaceWith.toString(); + return "Removing all occurences of ingredient: " + toReplace + " and replacing them with " + replaceWithName; + } + + private IIngredient changeIngredient(IIngredient input) { + List inputItems = input.getItems(); + if (inputItems.size() == 1) { // to avoid SingletonList#removeIf throwing UnsupportedOperationException + if (toReplace.matches(inputItems.get(0))) { + return replaceWith; + } + } else if (inputItems.size() > 1) { + if (inputItems.removeIf(toReplace::matches)) { + if (inputItems.isEmpty()) { + return replaceWith; + } else { + IIngredient temp = new IngredientOr(inputItems.toArray(new IIngredient[0])); + return replaceWith == null ? temp : temp.or(replaceWith); + } + } + } + return input; + } + + private void applyShapelessPattern() { + if (ActionReplaceAllOccurences.INSTANCE.ingredients1D != null) { + for (int i = 0; i < ActionReplaceAllOccurences.INSTANCE.ingredients1D.length; i++) { + IIngredient ingredient = ActionReplaceAllOccurences.INSTANCE.ingredients1D[i]; + if (ingredient == null) + continue; + IIngredient changeResult = changeIngredient(ingredient); + if (changeResult != ingredient) { + if (changeResult == null) { + ActionReplaceAllOccurences.INSTANCE.ingredients1D = ArrayUtils.remove(ActionReplaceAllOccurences.INSTANCE.ingredients1D, i); + } else { + ActionReplaceAllOccurences.INSTANCE.ingredients1D[i] = changeResult; + } + ActionReplaceAllOccurences.INSTANCE.recipeModified = true; + } + } + } + } + + private void applyShapedPattern() { + if (ActionReplaceAllOccurences.INSTANCE.ingredients2D != null) { + for (int i = 0; i < ActionReplaceAllOccurences.INSTANCE.ingredients2D.length; i++) { + for (int j = 0; j < ActionReplaceAllOccurences.INSTANCE.ingredients2D[i].length; j++) { + IIngredient ingredient = ActionReplaceAllOccurences.INSTANCE.ingredients2D[i][j]; + if (ingredient == null) + continue; + IIngredient changeResult = changeIngredient(ingredient); + if (changeResult != ingredient) { + ActionReplaceAllOccurences.INSTANCE.ingredients2D[i][j] = changeResult; + ActionReplaceAllOccurences.INSTANCE.recipeModified = true; + } + } + } + } + } + } + + public enum ActionReplaceAllOccurences implements IAction { + INSTANCE; + + private SubActionReplaceAllOccurences first; + private SubActionReplaceAllOccurences last; + private ICraftingRecipe currentModifiedRecipe; + private IIngredient[] ingredients1D; + private IIngredient[][] ingredients2D; + private boolean recipeModified = false; + + public void setCurrentModifiedRecipe(ICraftingRecipe currentModifiedRecipe) { + if (currentModifiedRecipe == null || currentModifiedRecipe.getOutput() == null) + return; + if (actionRemoveRecipesNoIngredients.matches(currentModifiedRecipe.getOutput())) { + return; // if there is a `recipes.remove` wants to remove origin recipe, then there is no need to replace the recipe + } + this.currentModifiedRecipe = currentModifiedRecipe; + if (currentModifiedRecipe.isShaped()) { + this.ingredients2D = currentModifiedRecipe.getIngredients2D(); + } else { + this.ingredients1D = currentModifiedRecipe.getIngredients1D(); + } + } + + public void addSubAction(SubActionReplaceAllOccurences subAction) { + if (first == null) { + first = subAction; + } + if (last != null) { + last.next = subAction; + } + last = subAction; + } + + public boolean hasSubAction() { + return first != null; + } + + public void clear() { + first = last = null; + } + + @Override + public void apply() { + SubActionReplaceAllOccurences current = first; + + while (current != null) { + current.apply(); + current = current.next; + } + + if (this.recipeModified) { + CraftTweakerAPI.recipes.removeByRecipeName(this.currentModifiedRecipe.getFullResourceName(), null); + String name = this.currentModifiedRecipe.getResourceDomain() + "_" + this.currentModifiedRecipe.getName() + "_modified"; + IItemStack out = this.currentModifiedRecipe.getOutput(); + if (this.currentModifiedRecipe.isShaped()) { + CraftTweakerAPI.recipes.addShaped(name, out, ingredients2D, null, null); + } else { + CraftTweakerAPI.recipes.addShapeless(name, out, ingredients1D, null, null); + } + } + + this.currentModifiedRecipe = null; + this.ingredients1D = null; + this.ingredients2D = null; + this.recipeModified = false; + } + + public void describeSubActions() { + SubActionReplaceAllOccurences current = first; + + while (current != null) { + CraftTweakerAPI.logInfo(current.describe()); + current = current.next; + } + } + + @Override + public String describe() { + return null; + } + } private static class ContainerVirtual extends Container {