diff options
author | Glease <4586901+Glease@users.noreply.github.com> | 2022-09-01 19:25:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-01 13:25:03 +0200 |
commit | 21b803f324ade7b357998c1f712df538b244c4b9 (patch) | |
tree | dc2e318669a5a9743deac8a7af3dbedcb3e702c9 | |
parent | b1ac2dc90ec6847e5a328fb12a3ece3b670df33c (diff) | |
download | GT5-Unofficial-21b803f324ade7b357998c1f712df538b244c4b9.tar.gz GT5-Unofficial-21b803f324ade7b357998c1f712df538b244c4b9.tar.bz2 GT5-Unofficial-21b803f324ade7b357998c1f712df538b244c4b9.zip |
various loading time optimization (#1330)
* change to more performant GT_ItemStack2 in some maps
The exact performance improvement of this is hard to measure as it impacts both startup time and tick time, but in general identityHashCode should be faster than retrieving stuff from registry.
* improve performance for registerUsagesForMaterials
index the recipe list based on recipe type, so we no longer do 16*44*444 recipe match. this indexing reduced the total runtime from ~12 second to ~3.7 second.
* guess this not belongs here
* move type check earlier
* more GT_ItemStack2
Co-authored-by: Martin Robertz <dream-master@gmx.net>
-rw-r--r-- | src/main/java/gregtech/GT_Mod.java | 6 | ||||
-rw-r--r-- | src/main/java/gregtech/api/GregTech_API.java | 2 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_OreDictUnificator.java | 24 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_RecipeRegistrator.java | 127 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_Utility.java | 28 |
5 files changed, 154 insertions, 33 deletions
diff --git a/src/main/java/gregtech/GT_Mod.java b/src/main/java/gregtech/GT_Mod.java index 23ef836960..1d1f8cb0d6 100644 --- a/src/main/java/gregtech/GT_Mod.java +++ b/src/main/java/gregtech/GT_Mod.java @@ -29,6 +29,7 @@ import gregtech.api.enums.Materials; import gregtech.api.enums.OrePrefixes; import gregtech.api.enums.Textures; import gregtech.api.interfaces.internal.IGT_Mod; +import gregtech.api.objects.GT_ItemStack; import gregtech.api.objects.ItemData; import gregtech.api.objects.ReverseShapedRecipe; import gregtech.api.objects.ReverseShapelessRecipe; @@ -751,9 +752,8 @@ public class GT_Mod implements IGT_Mod { GT_Utility.reInit(); GT_Recipe.reInit(); try { - for (Map<gregtech.api.objects.GT_ItemStack, ?> gt_itemStackMap : GregTech_API.sItemStackMappings) { - //noinspection rawtypes,unchecked// Deal with legacy Minecraft raw types,rawtypes - GT_Utility.reMap((Map) gt_itemStackMap); + for (Map<? extends GT_ItemStack, ?> gt_itemStackMap : GregTech_API.sItemStackMappings) { + GT_Utility.reMap(gt_itemStackMap); } } catch (Throwable e) { e.printStackTrace(GT_Log.err); diff --git a/src/main/java/gregtech/api/GregTech_API.java b/src/main/java/gregtech/api/GregTech_API.java index 56e80d4971..b565190507 100644 --- a/src/main/java/gregtech/api/GregTech_API.java +++ b/src/main/java/gregtech/api/GregTech_API.java @@ -99,7 +99,7 @@ public class GregTech_API { /** * Fixes the HashMap Mappings for ItemStacks once the Server started */ - public static final Collection<Map<GT_ItemStack, ?>> sItemStackMappings = new ArrayList<>(); + public static final Collection<Map<? extends GT_ItemStack, ?>> sItemStackMappings = new ArrayList<>(); public static final Collection<Map<Fluid, ?>> sFluidMappings = new ArrayList<>(); /** diff --git a/src/main/java/gregtech/api/util/GT_OreDictUnificator.java b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java index 81038645e9..48882bb72f 100644 --- a/src/main/java/gregtech/api/util/GT_OreDictUnificator.java +++ b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java @@ -9,16 +9,18 @@ import gregtech.api.enums.Dyes; import gregtech.api.enums.Materials; import gregtech.api.enums.OrePrefixes; import gregtech.api.enums.SubTag; -import gregtech.api.objects.GT_HashSet; import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.GT_ItemStack2; import gregtech.api.objects.ItemData; import gregtech.api.objects.MaterialStack; 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.Map.Entry; +import java.util.Set; import javax.annotation.Nullable; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -35,9 +37,9 @@ import net.minecraftforge.oredict.OreDictionary; */ public class GT_OreDictUnificator { private static final Map<String, ItemStack> sName2StackMap = new HashMap<>(); - private static final Map<GT_ItemStack, ItemData> sItemStack2DataMap = new HashMap<>(); - private static final Map<GT_ItemStack, List<ItemStack>> sUnificationTable = new HashMap<>(); - private static final GT_HashSet<GT_ItemStack> sNoUnificationList = new GT_HashSet<>(); + private static final Map<GT_ItemStack2, ItemData> sItemStack2DataMap = new HashMap<>(); + private static final Map<GT_ItemStack2, List<ItemStack>> sUnificationTable = new HashMap<>(); + private static final Set<GT_ItemStack2> sNoUnificationList = new HashSet<>(); public static volatile int VERSION = 509; private static int isRegisteringOre = 0, isAddingOre = 0; private static boolean mRunThroughTheList = true; @@ -53,7 +55,7 @@ public class GT_OreDictUnificator { */ public static void addToBlacklist(ItemStack aStack) { if (GT_Utility.isStackValid(aStack) && !GT_Utility.isStackInList(aStack, sNoUnificationList)) - sNoUnificationList.add(aStack); + sNoUnificationList.add(new GT_ItemStack2(aStack)); } public static boolean isBlacklisted(ItemStack aStack) { @@ -250,7 +252,7 @@ public class GT_OreDictUnificator { ItemStack tStack0 = tGTStack0.toStack(); ItemStack tStack1 = get_nocopy(false, tStack0); if (!GT_Utility.areStacksEqual(tStack0, tStack1)) { - GT_ItemStack tGTStack1 = new GT_ItemStack(tStack1); + GT_ItemStack2 tGTStack1 = new GT_ItemStack2(tStack1); List<ItemStack> list = sUnificationTable.computeIfAbsent(tGTStack1, k -> new ArrayList<>()); // greg's original code tries to dedupe the list using List#contains, which won't work // on vanilla ItemStack. I removed it since it never worked and can be slow. @@ -267,7 +269,7 @@ public class GT_OreDictUnificator { List<ItemStack> rList = new ArrayList<>(); for (ItemStack aStack : aStacks) { rList.add(aStack); - List<ItemStack> tList = sUnificationTable.get(new GT_ItemStack(aStack)); + List<ItemStack> tList = sUnificationTable.get(new GT_ItemStack2(aStack)); if (tList != null) { for (ItemStack tStack : tList) { ItemStack tStack1 = GT_Utility.copyAmount(aStack.stackSize, tStack); @@ -294,7 +296,7 @@ public class GT_OreDictUnificator { for (MaterialStack tMaterial : aData.mByProducts) tMaterial.mAmount /= aStack.stackSize; aStack = GT_Utility.copyAmount(1, aStack); } - sItemStack2DataMap.put(new GT_ItemStack(aStack), aData); + sItemStack2DataMap.put(new GT_ItemStack2(aStack), aData); if (aData.hasValidMaterialData()) { long tValidMaterialAmount = aData.mMaterial.mMaterial.contains(SubTag.NO_RECYCLING) ? 0 @@ -308,7 +310,7 @@ public class GT_OreDictUnificator { if (mRunThroughTheList) { if (GregTech_API.sLoadStarted) { mRunThroughTheList = false; - for (Entry<GT_ItemStack, ItemData> tEntry : sItemStack2DataMap.entrySet()) + for (Entry<GT_ItemStack2, ItemData> tEntry : sItemStack2DataMap.entrySet()) if (!tEntry.getValue().hasValidPrefixData() || tEntry.getValue().mPrefix.mAllowNormalRecycling) GT_RecipeRegistrator.registerMaterialRecycling( tEntry.getKey().toStack(), tEntry.getValue()); @@ -335,8 +337,8 @@ public class GT_OreDictUnificator { public static ItemData getItemData(ItemStack aStack) { if (GT_Utility.isStackInvalid(aStack)) return null; - ItemData rData = sItemStack2DataMap.get(new GT_ItemStack(aStack)); - if (rData == null) rData = sItemStack2DataMap.get(new GT_ItemStack(aStack, true)); + ItemData rData = sItemStack2DataMap.get(new GT_ItemStack2(aStack)); + if (rData == null) rData = sItemStack2DataMap.get(new GT_ItemStack2(aStack, true)); return rData; } diff --git a/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java index 1b1654d8a6..a01ee332ae 100644 --- a/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java +++ b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java @@ -4,17 +4,25 @@ import static gregtech.api.enums.GT_Values.*; import static gregtech.api.enums.Materials.*; import static gregtech.api.enums.Materials.Void; +import com.google.common.collect.ImmutableList; import gregtech.GT_Mod; import gregtech.api.GregTech_API; import gregtech.api.enums.*; import gregtech.api.objects.ItemData; import gregtech.api.objects.MaterialStack; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import ic2.api.reactor.IReactorComponent; +import java.util.*; +import java.util.stream.Collectors; import net.minecraft.init.Blocks; import net.minecraft.init.Items; +import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; /** * Class for Automatic Recipe registering. @@ -73,6 +81,7 @@ public class GT_RecipeRegistrator { new RecipeShape(sMt1, sMt1, null, sMt2, null, sMt1, sMt2, null, null), new RecipeShape(null, sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2) }; + private static volatile Map<RecipeShape, List<IRecipe>> indexedRecipeListCache; private static final String[][] sShapesA = new String[][] { null, null, @@ -121,6 +130,11 @@ public class GT_RecipeRegistrator { }; public static volatile int VERSION = 509; + static { + // flush the cache on post load finish + GregTech_API.sAfterGTPostload.add(() -> indexedRecipeListCache = null); + } + public static void registerMaterialRecycling( ItemStack aStack, Materials aMaterial, long aMaterialAmount, MaterialStack aByproduct) { if (GT_Utility.isStackInvalid(aStack)) return; @@ -445,6 +459,102 @@ public class GT_RecipeRegistrator { } } + private static List<IRecipe> getRecipeList(RecipeShape shape) { + boolean force = !GregTech_API.sPostloadStarted || GregTech_API.sPostloadFinished; + if (force || indexedRecipeListCache == null) { + synchronized (GT_RecipeRegistrator.class) { + if (indexedRecipeListCache == null || force) { + indexedRecipeListCache = createIndexedRecipeListCache(); + } + } + } + return indexedRecipeListCache.get(shape); + } + + private static Map<RecipeShape, List<IRecipe>> createIndexedRecipeListCache() { + Map<RecipeShape, List<IRecipe>> result = new IdentityHashMap<>(); + @SuppressWarnings("unchecked") + ArrayList<IRecipe> allRecipeList = + (ArrayList<IRecipe>) CraftingManager.getInstance().getRecipeList(); + // filter using the empty slots in the shape. + // if the empty slots doesn't match, the recipe will definitely fail + Map<List<Integer>, List<RecipeShape>> filter = + Arrays.stream(sShapes).collect(Collectors.groupingBy(RecipeShape::getEmptySlots)); + List<Integer> x = new ArrayList<>(9); + for (IRecipe tRecipe : allRecipeList) { + if (tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe) { + // we don't target shapeless recipes + continue; + } + x.clear(); + ItemStack tStack = tRecipe.getRecipeOutput(); + if (GT_Utility.isStackValid(tStack) + && tStack.getMaxStackSize() == 1 + && tStack.getMaxDamage() > 0 + && !(tStack.getItem() instanceof ItemBlock) + && !(tStack.getItem() instanceof IReactorComponent) + && !GT_ModHandler.isElectricItem(tStack) + && !GT_Utility.isStackInList(tStack, GT_ModHandler.sNonReplaceableItems)) { + if (tRecipe instanceof ShapedOreRecipe) { + boolean allValid = true; + Object[] input = ((ShapedOreRecipe) tRecipe).getInput(); + for (int i = 0, inputLength = input.length; i < inputLength; i++) { + Object tObject = input[i]; + if (tObject != null) { + if (tObject instanceof ItemStack + && (((ItemStack) tObject).getItem() == null + || ((ItemStack) tObject).getMaxStackSize() < 2 + || ((ItemStack) tObject).getMaxDamage() > 0 + || ((ItemStack) tObject).getItem() instanceof ItemBlock)) { + allValid = false; + break; + } + if (tObject instanceof List && ((List<?>) tObject).isEmpty()) { + allValid = false; + break; + } + } else { + x.add(i); + } + } + if (allValid) { + for (RecipeShape s : filter.getOrDefault(x, Collections.emptyList())) { + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } else if (tRecipe instanceof ShapedRecipes) { + boolean allValid = true; + ItemStack[] recipeItems = ((ShapedRecipes) tRecipe).recipeItems; + for (int i = 0, recipeItemsLength = recipeItems.length; i < recipeItemsLength; i++) { + ItemStack tObject = recipeItems[i]; + if (tObject != null + && (tObject.getItem() == null + || tObject.getMaxStackSize() < 2 + || tObject.getMaxDamage() > 0 + || tObject.getItem() instanceof ItemBlock)) { + allValid = false; + break; + } else { + x.add(i); + } + } + if (allValid) { + for (RecipeShape s : filter.getOrDefault(x, Collections.emptyList())) { + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } else { + for (RecipeShape s : sShapes) { + // unknown recipe type. cannot determine empty slots. we choose to add to the recipe list for + // all shapes + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } + } + return result; + } + private static synchronized void registerStickStuff(String aPlate, ItemData aItemData, boolean aRecipeReplacing) { ItemStack tStack; for (Materials tMaterial : sRodMaterialList) { @@ -457,7 +567,8 @@ public class GT_RecipeRegistrator { for (int i = 0; i < sShapes.length; i++) { RecipeShape tRecipe = sShapes[i]; - for (ItemStack tCrafted : GT_ModHandler.getVanillyToolRecipeOutputs(tRecipe.shape)) { + for (ItemStack tCrafted : + GT_ModHandler.getRecipeOutputs(getRecipeList(tRecipe), true, tRecipe.shape)) { if (aItemData != null && aItemData.hasValidPrefixMaterialData()) GT_OreDictUnificator.addItemData( tCrafted, @@ -547,5 +658,13 @@ public class GT_RecipeRegistrator { if (stack == sMt2) this.amount2++; } } + + public List<Integer> getEmptySlots() { + ImmutableList.Builder<Integer> b = ImmutableList.builder(); + for (int i = 0; i < shape.length; i++) { + if (shape[i] == null) b.add(i); + } + return b.build(); + } } } diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java index 50106795eb..b35b703ab9 100644 --- a/src/main/java/gregtech/api/util/GT_Utility.java +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -44,6 +44,7 @@ import gregtech.api.items.GT_MetaGenerated_Tool; import gregtech.api.net.GT_Packet_Sound; import gregtech.api.objects.CollectorUtils; import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.GT_ItemStack2; import gregtech.api.objects.ItemData; import gregtech.api.threads.GT_Runnable_Sound; import gregtech.api.util.extensions.ArrayExt; @@ -60,21 +61,8 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; @@ -2933,11 +2921,23 @@ public class GT_Utility { return isStackInList(new GT_ItemStack(aStack), aList); } + public static boolean isStackInList(ItemStack aStack, Set<GT_ItemStack2> aList) { + if (aStack == null) { + return false; + } + return isStackInList(new GT_ItemStack2(aStack), aList); + } + public static boolean isStackInList(GT_ItemStack aStack, Collection<GT_ItemStack> aList) { return aStack != null && (aList.contains(aStack) || aList.contains(new GT_ItemStack(aStack.mItem, aStack.mStackSize, W))); } + public static boolean isStackInList(GT_ItemStack2 aStack, Set<GT_ItemStack2> aList) { + return aStack != null + && (aList.contains(aStack) || aList.contains(new GT_ItemStack2(aStack.mItem, aStack.mStackSize, W))); + } + /** * re-maps all Keys of a Map after the Keys were weakened. */ |