RecipeFinder.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * Roughly Enough Items by Danielshe.
  3. * Licensed under the MIT License.
  4. */
  5. package me.shedaniel.rei.server;
  6. import com.google.common.collect.Lists;
  7. import it.unimi.dsi.fastutil.ints.*;
  8. import net.minecraft.item.Item;
  9. import net.minecraft.item.ItemStack;
  10. import net.minecraft.recipe.Ingredient;
  11. import net.minecraft.util.DefaultedList;
  12. import net.minecraft.util.registry.Registry;
  13. import javax.annotation.Nullable;
  14. import java.util.BitSet;
  15. import java.util.Iterator;
  16. import java.util.List;
  17. import java.util.stream.Collectors;
  18. public class RecipeFinder {
  19. public final Int2IntMap idToAmountMap = new Int2IntOpenHashMap();
  20. public static int getItemId(ItemStack itemStack_1) {
  21. return Registry.ITEM.getRawId(itemStack_1.getItem());
  22. }
  23. public static ItemStack getStackFromId(int int_1) {
  24. return int_1 == 0 ? ItemStack.EMPTY : new ItemStack(Item.byRawId(int_1));
  25. }
  26. public void addNormalItem(ItemStack itemStack_1) {
  27. if (!itemStack_1.isDamaged() && !itemStack_1.hasEnchantments() && !itemStack_1.hasCustomName()) {
  28. this.addItem(itemStack_1);
  29. }
  30. }
  31. public void addItem(ItemStack itemStack_1) {
  32. this.addItem(itemStack_1, 64);
  33. }
  34. public void addItem(ItemStack itemStack_1, int int_1) {
  35. if (!itemStack_1.isEmpty()) {
  36. int itemId = getItemId(itemStack_1);
  37. int itemCount = Math.min(int_1, itemStack_1.getCount());
  38. this.addItem(itemId, itemCount);
  39. }
  40. }
  41. private boolean contains(int itemId) {
  42. return this.idToAmountMap.get(itemId) > 0;
  43. }
  44. /**
  45. * Takes an amount from the finder
  46. *
  47. * @return the amount taken
  48. */
  49. private int take(int itemId, int amount) {
  50. int mapAmount = this.idToAmountMap.get(itemId);
  51. if (mapAmount >= amount) {
  52. this.idToAmountMap.put(itemId, mapAmount - amount);
  53. return itemId;
  54. } else {
  55. return 0;
  56. }
  57. }
  58. private void addItem(int itemId, int itemCount) {
  59. this.idToAmountMap.put(itemId, this.idToAmountMap.get(itemId) + itemCount);
  60. }
  61. public boolean findRecipe(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1) {
  62. return this.findRecipe(ingredients, intList_1, 1);
  63. }
  64. public boolean findRecipe(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1, int int_1) {
  65. return (new RecipeFinder.Filter(ingredients)).find(int_1, intList_1);
  66. }
  67. public int countRecipeCrafts(DefaultedList<Ingredient> ingredients, @Nullable IntList intList_1) {
  68. return this.countRecipeCrafts(ingredients, Integer.MAX_VALUE, intList_1);
  69. }
  70. public int countRecipeCrafts(DefaultedList<Ingredient> ingredients, int int_1, @Nullable IntList intList_1) {
  71. return (new RecipeFinder.Filter(ingredients)).countCrafts(int_1, intList_1);
  72. }
  73. public void clear() {
  74. this.idToAmountMap.clear();
  75. }
  76. class Filter {
  77. private final List<Ingredient> ingredients = Lists.newArrayList();
  78. private final int ingredientCount;
  79. private final int[] usableIngredientItemIds;
  80. private final int usableIngredientSize;
  81. private final BitSet bitSet;
  82. private final IntList field_7557 = new IntArrayList();
  83. private final DefaultedList<Ingredient> ingredientsInput;
  84. public Filter(DefaultedList<Ingredient> ingredientsInput) {
  85. this.ingredientsInput = ingredientsInput;
  86. this.ingredients.addAll(ingredientsInput.stream().collect(Collectors.toList()));
  87. this.ingredients.removeIf(Ingredient::isEmpty);
  88. this.ingredientCount = this.ingredients.size();
  89. this.usableIngredientItemIds = this.getUsableIngredientItemIds();
  90. this.usableIngredientSize = this.usableIngredientItemIds.length;
  91. this.bitSet = new BitSet(this.ingredientCount + this.usableIngredientSize + this.ingredientCount + this.ingredientCount * this.usableIngredientSize);
  92. for (int ingredientIndex = 0; ingredientIndex < this.ingredients.size(); ++ingredientIndex) {
  93. IntList possibleStacks = ((Ingredient) this.ingredients.get(ingredientIndex)).getIds();
  94. // Loops over usable ingredients
  95. for (int usableIngredientIndex = 0; usableIngredientIndex < this.usableIngredientSize; ++usableIngredientIndex) {
  96. if (possibleStacks.contains(this.usableIngredientItemIds[usableIngredientIndex])) {
  97. this.bitSet.set(this.method_7420(true, usableIngredientIndex, ingredientIndex));
  98. }
  99. }
  100. }
  101. }
  102. public boolean find(int int_1, @Nullable IntList intList_1) {
  103. if (int_1 <= 0) {
  104. return true;
  105. } else {
  106. int int_2;
  107. for (int_2 = 0; this.method_7423(int_1); ++int_2) {
  108. RecipeFinder.this.take(this.usableIngredientItemIds[this.field_7557.getInt(0)], int_1);
  109. int int_3 = this.field_7557.size() - 1;
  110. this.method_7421(this.field_7557.getInt(int_3));
  111. for (int int_4 = 0; int_4 < int_3; ++int_4) {
  112. this.method_7414((int_4 & 1) == 0, this.field_7557.get(int_4), this.field_7557.get(int_4 + 1));
  113. }
  114. this.field_7557.clear();
  115. this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize);
  116. }
  117. boolean boolean_1 = int_2 == this.ingredientCount;
  118. boolean boolean_2 = boolean_1 && intList_1 != null;
  119. if (boolean_2) {
  120. intList_1.clear();
  121. }
  122. this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize + this.ingredientCount);
  123. int int_5 = 0;
  124. List<Ingredient> list_1 = ingredientsInput.stream().collect(Collectors.toList());
  125. for (int int_6 = 0; int_6 < list_1.size(); ++int_6) {
  126. if (boolean_2 && ((Ingredient) list_1.get(int_6)).isEmpty()) {
  127. intList_1.add(0);
  128. } else {
  129. for (int int_7 = 0; int_7 < this.usableIngredientSize; ++int_7) {
  130. if (this.method_7425(false, int_5, int_7)) {
  131. this.method_7414(true, int_7, int_5);
  132. RecipeFinder.this.addItem(this.usableIngredientItemIds[int_7], int_1);
  133. if (boolean_2) {
  134. intList_1.add(this.usableIngredientItemIds[int_7]);
  135. }
  136. }
  137. }
  138. ++int_5;
  139. }
  140. }
  141. return boolean_1;
  142. }
  143. }
  144. private int[] getUsableIngredientItemIds() {
  145. IntCollection intCollection_1 = new IntAVLTreeSet();
  146. Iterator var2 = this.ingredients.iterator();
  147. while (var2.hasNext()) {
  148. Ingredient ingredient_1 = (Ingredient) var2.next();
  149. intCollection_1.addAll(ingredient_1.getIds());
  150. }
  151. IntIterator intIterator_1 = intCollection_1.iterator();
  152. while (intIterator_1.hasNext()) {
  153. if (!RecipeFinder.this.contains(intIterator_1.nextInt())) {
  154. intIterator_1.remove();
  155. }
  156. }
  157. return intCollection_1.toIntArray();
  158. }
  159. private boolean method_7423(int int_1) {
  160. int usableIngredientSize = this.usableIngredientSize;
  161. for (int int_3 = 0; int_3 < usableIngredientSize; ++int_3) {
  162. if (RecipeFinder.this.idToAmountMap.get(this.usableIngredientItemIds[int_3]) >= int_1) {
  163. this.method_7413(false, int_3);
  164. while (!this.field_7557.isEmpty()) {
  165. int int_4 = this.field_7557.size();
  166. boolean boolean_1 = (int_4 & 1) == 1;
  167. int int_5 = this.field_7557.getInt(int_4 - 1);
  168. if (!boolean_1 && !this.method_7416(int_5)) {
  169. break;
  170. }
  171. int int_6 = boolean_1 ? this.ingredientCount : usableIngredientSize;
  172. int int_8;
  173. for (int_8 = 0; int_8 < int_6; ++int_8) {
  174. 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)) {
  175. this.method_7413(boolean_1, int_8);
  176. break;
  177. }
  178. }
  179. int_8 = this.field_7557.size();
  180. if (int_8 == int_4) {
  181. this.field_7557.removeInt(int_8 - 1);
  182. }
  183. }
  184. if (!this.field_7557.isEmpty()) {
  185. return true;
  186. }
  187. }
  188. }
  189. return false;
  190. }
  191. private boolean method_7416(int int_1) {
  192. return this.bitSet.get(this.method_7419(int_1));
  193. }
  194. private void method_7421(int int_1) {
  195. this.bitSet.set(this.method_7419(int_1));
  196. }
  197. private int method_7419(int int_1) {
  198. return this.ingredientCount + this.usableIngredientSize + int_1;
  199. }
  200. private boolean method_7418(boolean boolean_1, int int_1, int int_2) {
  201. return this.bitSet.get(this.method_7420(boolean_1, int_1, int_2));
  202. }
  203. private boolean method_7425(boolean boolean_1, int int_1, int int_2) {
  204. return boolean_1 != this.bitSet.get(1 + this.method_7420(boolean_1, int_1, int_2));
  205. }
  206. private void method_7414(boolean boolean_1, int int_1, int int_2) {
  207. this.bitSet.flip(1 + this.method_7420(boolean_1, int_1, int_2));
  208. }
  209. private int method_7420(boolean boolean_1, int int_1, int int_2) {
  210. int int_3 = boolean_1 ? int_1 * this.ingredientCount + int_2 : int_2 * this.ingredientCount + int_1;
  211. return this.ingredientCount + this.usableIngredientSize + this.ingredientCount + 2 * int_3;
  212. }
  213. private void method_7413(boolean boolean_1, int int_1) {
  214. this.bitSet.set(this.method_7424(boolean_1, int_1));
  215. this.field_7557.add(int_1);
  216. }
  217. private boolean method_7426(boolean boolean_1, int int_1) {
  218. return this.bitSet.get(this.method_7424(boolean_1, int_1));
  219. }
  220. private int method_7424(boolean boolean_1, int int_1) {
  221. return (boolean_1 ? 0 : this.ingredientCount) + int_1;
  222. }
  223. public int countCrafts(int int_1, @Nullable IntList intList_1) {
  224. int int_2 = 0;
  225. int int_3 = Math.min(int_1, this.method_7415()) + 1;
  226. while (true) {
  227. while (true) {
  228. int int_4 = (int_2 + int_3) / 2;
  229. if (this.find(int_4, (IntList) null)) {
  230. if (int_3 - int_2 <= 1) {
  231. if (int_4 > 0) {
  232. this.find(int_4, intList_1);
  233. }
  234. return int_4;
  235. }
  236. int_2 = int_4;
  237. } else {
  238. int_3 = int_4;
  239. }
  240. }
  241. }
  242. }
  243. private int method_7415() {
  244. int int_1 = Integer.MAX_VALUE;
  245. Iterator var2 = this.ingredients.iterator();
  246. while (var2.hasNext()) {
  247. Ingredient ingredient_1 = (Ingredient) var2.next();
  248. int int_2 = 0;
  249. int int_3;
  250. for (IntListIterator var5 = ingredient_1.getIds().iterator(); var5.hasNext(); int_2 = Math.max(int_2, RecipeFinder.this.idToAmountMap.get(int_3))) {
  251. int_3 = (Integer) var5.next();
  252. }
  253. if (int_1 > 0) {
  254. int_1 = Math.min(int_1, int_2);
  255. }
  256. }
  257. return int_1;
  258. }
  259. }
  260. }