RecipeFinder.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * This file is licensed under the MIT License, part of Roughly Enough Items.
  3. * Copyright (c) 2018, 2019, 2020 shedaniel
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. package me.shedaniel.rei.server;
  24. import com.google.common.collect.Lists;
  25. import it.unimi.dsi.fastutil.ints.*;
  26. import net.minecraft.item.Item;
  27. import net.minecraft.item.ItemStack;
  28. import net.minecraft.recipe.Ingredient;
  29. import net.minecraft.util.collection.DefaultedList;
  30. import net.minecraft.util.registry.Registry;
  31. import org.jetbrains.annotations.ApiStatus;
  32. import org.jetbrains.annotations.Nullable;
  33. import java.util.ArrayList;
  34. import java.util.BitSet;
  35. import java.util.Iterator;
  36. import java.util.List;
  37. import java.util.stream.Collectors;
  38. @ApiStatus.Internal
  39. public class RecipeFinder {
  40. public final Int2IntMap idToAmountMap = new Int2IntOpenHashMap();
  41. public static int getItemId(ItemStack itemStack_1) {
  42. return Registry.ITEM.getRawId(itemStack_1.getItem());
  43. }
  44. public static ItemStack getStackFromId(int int_1) {
  45. return int_1 == 0 ? ItemStack.EMPTY : new ItemStack(Item.byRawId(int_1));
  46. }
  47. public void addNormalItem(ItemStack itemStack_1) {
  48. if (!itemStack_1.isDamaged() && !itemStack_1.hasEnchantments() && !itemStack_1.hasCustomName()) {
  49. this.addItem(itemStack_1);
  50. }
  51. }
  52. public void addItem(ItemStack itemStack_1) {
  53. this.addItem(itemStack_1, 64);
  54. }
  55. public void addItem(ItemStack itemStack_1, int int_1) {
  56. if (!itemStack_1.isEmpty()) {
  57. int itemId = getItemId(itemStack_1);
  58. int itemCount = Math.min(int_1, itemStack_1.getCount());
  59. this.addItem(itemId, itemCount);
  60. }
  61. }
  62. private boolean contains(int itemId) {
  63. return this.idToAmountMap.get(itemId) > 0;
  64. }
  65. /**
  66. * Takes an amount from the finder
  67. *
  68. * @return the amount taken
  69. */
  70. private int take(int itemId, int amount) {
  71. int mapAmount = this.idToAmountMap.get(itemId);
  72. if (mapAmount >= amount) {
  73. this.idToAmountMap.put(itemId, mapAmount - amount);
  74. return itemId;
  75. } else {
  76. return 0;
  77. }
  78. }
  79. private void addItem(int itemId, int itemCount) {
  80. this.idToAmountMap.put(itemId, this.idToAmountMap.get(itemId) + itemCount);
  81. }
  82. public boolean findRecipe(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1) {
  83. return this.findRecipe(ingredients, intList_1, 1);
  84. }
  85. public boolean findRecipe(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1, int int_1) {
  86. return (new RecipeFinder.Filter(ingredients)).find(int_1, intList_1);
  87. }
  88. public int countRecipeCrafts(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1) {
  89. return this.countRecipeCrafts(ingredients, Integer.MAX_VALUE, intList_1);
  90. }
  91. public int countRecipeCrafts(DefaultedList<Ingredient> ingredients, int int_1, @Nullable IntList intList_1) {
  92. return (new RecipeFinder.Filter(ingredients)).countCrafts(int_1, intList_1);
  93. }
  94. public void clear() {
  95. this.idToAmountMap.clear();
  96. }
  97. class Filter {
  98. private final List<Ingredient> ingredients = Lists.newArrayList();
  99. private final int ingredientCount;
  100. private final int[] usableIngredientItemIds;
  101. private final int usableIngredientSize;
  102. private final BitSet bitSet;
  103. private final IntList field_7557 = new IntArrayList();
  104. private final DefaultedList<Ingredient> ingredientsInput;
  105. public Filter(DefaultedList<Ingredient> ingredientsInput) {
  106. this.ingredientsInput = ingredientsInput;
  107. this.ingredients.addAll(new ArrayList<>(ingredientsInput));
  108. this.ingredients.removeIf(Ingredient::isEmpty);
  109. this.ingredientCount = this.ingredients.size();
  110. this.usableIngredientItemIds = this.getUsableIngredientItemIds();
  111. this.usableIngredientSize = this.usableIngredientItemIds.length;
  112. this.bitSet = new BitSet(this.ingredientCount + this.usableIngredientSize + this.ingredientCount + this.ingredientCount * this.usableIngredientSize);
  113. for (int ingredientIndex = 0; ingredientIndex < this.ingredients.size(); ++ingredientIndex) {
  114. IntList possibleStacks = this.ingredients.get(ingredientIndex).getIds();
  115. // Loops over usable ingredients
  116. for (int usableIngredientIndex = 0; usableIngredientIndex < this.usableIngredientSize; ++usableIngredientIndex) {
  117. if (possibleStacks.contains(this.usableIngredientItemIds[usableIngredientIndex])) {
  118. this.bitSet.set(this.method_7420(true, usableIngredientIndex, ingredientIndex));
  119. }
  120. }
  121. }
  122. }
  123. @SuppressWarnings("deprecation")
  124. public boolean find(int int_1, @Nullable IntList intList_1) {
  125. if (int_1 <= 0) {
  126. return true;
  127. } else {
  128. int int_2;
  129. for (int_2 = 0; this.method_7423(int_1); ++int_2) {
  130. RecipeFinder.this.take(this.usableIngredientItemIds[this.field_7557.getInt(0)], int_1);
  131. int int_3 = this.field_7557.size() - 1;
  132. this.method_7421(this.field_7557.getInt(int_3));
  133. for (int int_4 = 0; int_4 < int_3; ++int_4) {
  134. this.method_7414((int_4 & 1) == 0, this.field_7557.get(int_4), this.field_7557.get(int_4 + 1));
  135. }
  136. this.field_7557.clear();
  137. this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize);
  138. }
  139. boolean boolean_1 = int_2 == this.ingredientCount;
  140. boolean boolean_2 = boolean_1 && intList_1 != null;
  141. if (boolean_2) {
  142. intList_1.clear();
  143. }
  144. this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize + this.ingredientCount);
  145. int int_5 = 0;
  146. List<Ingredient> list_1 = ingredientsInput.stream().collect(Collectors.toList());
  147. for (int int_6 = 0; int_6 < list_1.size(); ++int_6) {
  148. if (boolean_2 && list_1.get(int_6).isEmpty()) {
  149. intList_1.add(0);
  150. } else {
  151. for (int int_7 = 0; int_7 < this.usableIngredientSize; ++int_7) {
  152. if (this.method_7425(false, int_5, int_7)) {
  153. this.method_7414(true, int_7, int_5);
  154. RecipeFinder.this.addItem(this.usableIngredientItemIds[int_7], int_1);
  155. if (boolean_2) {
  156. intList_1.add(this.usableIngredientItemIds[int_7]);
  157. }
  158. }
  159. }
  160. ++int_5;
  161. }
  162. }
  163. return boolean_1;
  164. }
  165. }
  166. private int[] getUsableIngredientItemIds() {
  167. IntCollection intCollection_1 = new IntAVLTreeSet();
  168. Iterator var2 = this.ingredients.iterator();
  169. while (var2.hasNext()) {
  170. Ingredient ingredient_1 = (Ingredient) var2.next();
  171. intCollection_1.addAll(ingredient_1.getIds());
  172. }
  173. IntIterator intIterator_1 = intCollection_1.iterator();
  174. while (intIterator_1.hasNext()) {
  175. if (!RecipeFinder.this.contains(intIterator_1.nextInt())) {
  176. intIterator_1.remove();
  177. }
  178. }
  179. return intCollection_1.toIntArray();
  180. }
  181. private boolean method_7423(int int_1) {
  182. int usableIngredientSize = this.usableIngredientSize;
  183. for (int int_3 = 0; int_3 < usableIngredientSize; ++int_3) {
  184. if (RecipeFinder.this.idToAmountMap.get(this.usableIngredientItemIds[int_3]) >= int_1) {
  185. this.method_7413(false, int_3);
  186. while (!this.field_7557.isEmpty()) {
  187. int int_4 = this.field_7557.size();
  188. boolean boolean_1 = (int_4 & 1) == 1;
  189. int int_5 = this.field_7557.getInt(int_4 - 1);
  190. if (!boolean_1 && !this.method_7416(int_5)) {
  191. break;
  192. }
  193. int int_6 = boolean_1 ? this.ingredientCount : usableIngredientSize;
  194. int int_8;
  195. for (int_8 = 0; int_8 < int_6; ++int_8) {
  196. if (!this.method_7426(boolean_1, int_8) && this.method_7418(boolean_1, int_5, int_8) && this.method_7425(boolean_1, int_5, int_8)) {
  197. this.method_7413(boolean_1, int_8);
  198. break;
  199. }
  200. }
  201. int_8 = this.field_7557.size();
  202. if (int_8 == int_4) {
  203. this.field_7557.removeInt(int_8 - 1);
  204. }
  205. }
  206. if (!this.field_7557.isEmpty()) {
  207. return true;
  208. }
  209. }
  210. }
  211. return false;
  212. }
  213. private boolean method_7416(int int_1) {
  214. return this.bitSet.get(this.method_7419(int_1));
  215. }
  216. private void method_7421(int int_1) {
  217. this.bitSet.set(this.method_7419(int_1));
  218. }
  219. private int method_7419(int int_1) {
  220. return this.ingredientCount + this.usableIngredientSize + int_1;
  221. }
  222. private boolean method_7418(boolean boolean_1, int int_1, int int_2) {
  223. return this.bitSet.get(this.method_7420(boolean_1, int_1, int_2));
  224. }
  225. private boolean method_7425(boolean boolean_1, int int_1, int int_2) {
  226. return boolean_1 != this.bitSet.get(1 + this.method_7420(boolean_1, int_1, int_2));
  227. }
  228. private void method_7414(boolean boolean_1, int int_1, int int_2) {
  229. this.bitSet.flip(1 + this.method_7420(boolean_1, int_1, int_2));
  230. }
  231. private int method_7420(boolean boolean_1, int int_1, int int_2) {
  232. int int_3 = boolean_1 ? int_1 * this.ingredientCount + int_2 : int_2 * this.ingredientCount + int_1;
  233. return this.ingredientCount + this.usableIngredientSize + this.ingredientCount + 2 * int_3;
  234. }
  235. private void method_7413(boolean boolean_1, int int_1) {
  236. this.bitSet.set(this.method_7424(boolean_1, int_1));
  237. this.field_7557.add(int_1);
  238. }
  239. private boolean method_7426(boolean boolean_1, int int_1) {
  240. return this.bitSet.get(this.method_7424(boolean_1, int_1));
  241. }
  242. private int method_7424(boolean boolean_1, int int_1) {
  243. return (boolean_1 ? 0 : this.ingredientCount) + int_1;
  244. }
  245. public int countCrafts(int int_1, @Nullable IntList intList_1) {
  246. int int_2 = 0;
  247. int int_3 = Math.min(int_1, this.method_7415()) + 1;
  248. while (true) {
  249. while (true) {
  250. int int_4 = (int_2 + int_3) / 2;
  251. if (this.find(int_4, null)) {
  252. if (int_3 - int_2 <= 1) {
  253. if (int_4 > 0) {
  254. this.find(int_4, intList_1);
  255. }
  256. return int_4;
  257. }
  258. int_2 = int_4;
  259. } else {
  260. int_3 = int_4;
  261. }
  262. }
  263. }
  264. }
  265. @SuppressWarnings("deprecation")
  266. private int method_7415() {
  267. int int_1 = Integer.MAX_VALUE;
  268. Iterator var2 = this.ingredients.iterator();
  269. while (var2.hasNext()) {
  270. Ingredient ingredient_1 = (Ingredient) var2.next();
  271. int int_2 = 0;
  272. int int_3;
  273. for (IntListIterator var5 = ingredient_1.getIds().iterator(); var5.hasNext(); int_2 = Math.max(int_2, RecipeFinder.this.idToAmountMap.get(int_3))) {
  274. int_3 = var5.next();
  275. }
  276. if (int_1 > 0) {
  277. int_1 = Math.min(int_1, int_2);
  278. }
  279. }
  280. return int_1;
  281. }
  282. }
  283. }