RecipeHelperImpl.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package me.shedaniel.rei.client;
  2. import com.google.common.collect.Lists;
  3. import com.google.common.collect.Maps;
  4. import me.shedaniel.rei.RoughlyEnoughItemsCore;
  5. import me.shedaniel.rei.api.*;
  6. import net.minecraft.item.ItemStack;
  7. import net.minecraft.recipe.RecipeManager;
  8. import net.minecraft.util.Identifier;
  9. import java.awt.*;
  10. import java.util.*;
  11. import java.util.List;
  12. import java.util.concurrent.atomic.AtomicInteger;
  13. import java.util.stream.Collectors;
  14. public class RecipeHelperImpl implements RecipeHelper {
  15. private static final Comparator VISIBILITY_HANDLER_COMPARATOR = Comparator.comparingDouble(value -> {
  16. if (value instanceof DisplayVisibilityHandler)
  17. return (double) ((DisplayVisibilityHandler) value).getPriority();
  18. return -Double.MAX_VALUE;
  19. }).reversed();
  20. private final AtomicInteger recipeCount = new AtomicInteger();
  21. private final Map<Identifier, List<RecipeDisplay>> recipeCategoryListMap = Maps.newHashMap();
  22. private final Map<Identifier, DisplaySettings> categoryDisplaySettingsMap = Maps.newHashMap();
  23. private final List<RecipeCategory> categories = Lists.newArrayList();
  24. private final Map<Identifier, ButtonAreaSupplier> speedCraftAreaSupplierMap = Maps.newHashMap();
  25. private final Map<Identifier, List<SpeedCraftFunctional>> speedCraftFunctionalMap = Maps.newHashMap();
  26. private final List<DisplayVisibilityHandler> displayVisibilityHandlers = Lists.newArrayList();
  27. private RecipeManager recipeManager;
  28. @Override
  29. public List<ItemStack> findCraftableByItems(List<ItemStack> inventoryItems) {
  30. List<ItemStack> craftables = new ArrayList<>();
  31. for(List<RecipeDisplay> value : recipeCategoryListMap.values())
  32. for(RecipeDisplay recipeDisplay : value) {
  33. int slotsCraftable = 0;
  34. List<List<ItemStack>> requiredInput = (List<List<ItemStack>>) recipeDisplay.getRequiredItems();
  35. for(List<ItemStack> slot : requiredInput) {
  36. if (slot.isEmpty()) {
  37. slotsCraftable++;
  38. continue;
  39. }
  40. boolean slotDone = false;
  41. for(ItemStack possibleType : inventoryItems) {
  42. for(ItemStack slotPossible : slot)
  43. if (ItemStack.areEqualIgnoreTags(slotPossible, possibleType)) {
  44. slotsCraftable++;
  45. slotDone = true;
  46. break;
  47. }
  48. if (slotDone)
  49. break;
  50. }
  51. }
  52. if (slotsCraftable == recipeDisplay.getRequiredItems().size())
  53. craftables.addAll((List<ItemStack>) recipeDisplay.getOutput());
  54. }
  55. return craftables.stream().distinct().collect(Collectors.toList());
  56. }
  57. @Override
  58. public void registerCategory(RecipeCategory category) {
  59. categories.add(category);
  60. categoryDisplaySettingsMap.put(category.getIdentifier(), category.getDisplaySettings());
  61. recipeCategoryListMap.put(category.getIdentifier(), Lists.newLinkedList());
  62. }
  63. @Override
  64. public void registerDisplay(Identifier categoryIdentifier, RecipeDisplay display) {
  65. if (!recipeCategoryListMap.containsKey(categoryIdentifier))
  66. return;
  67. recipeCount.incrementAndGet();
  68. recipeCategoryListMap.get(categoryIdentifier).add(display);
  69. }
  70. @Override
  71. public Map<RecipeCategory, List<RecipeDisplay>> getRecipesFor(ItemStack stack) {
  72. Map<Identifier, List<RecipeDisplay>> categoriesMap = new HashMap<>();
  73. categories.forEach(f -> categoriesMap.put(f.getIdentifier(), Lists.newArrayList()));
  74. for(Map.Entry<Identifier, List<RecipeDisplay>> entry : recipeCategoryListMap.entrySet()) {
  75. RecipeCategory category = getCategory(entry.getKey());
  76. for(RecipeDisplay recipeDisplay : entry.getValue())
  77. for(ItemStack outputStack : (List<ItemStack>) recipeDisplay.getOutput())
  78. if (category.checkTags() ? ItemStack.areEqual(stack, outputStack) : ItemStack.areEqualIgnoreTags(stack, outputStack))
  79. categoriesMap.get(recipeDisplay.getRecipeCategory()).add(recipeDisplay);
  80. }
  81. Map<RecipeCategory, List<RecipeDisplay>> recipeCategoryListMap = Maps.newLinkedHashMap();
  82. categories.forEach(category -> {
  83. if (categoriesMap.containsKey(category.getIdentifier()) && !categoriesMap.get(category.getIdentifier()).isEmpty())
  84. recipeCategoryListMap.put(category, categoriesMap.get(category.getIdentifier()).stream().filter(display -> isDisplayVisible(display, true)).collect(Collectors.toList()));
  85. });
  86. for(RecipeCategory category : Lists.newArrayList(recipeCategoryListMap.keySet()))
  87. if (recipeCategoryListMap.get(category).isEmpty())
  88. recipeCategoryListMap.remove(category);
  89. return recipeCategoryListMap;
  90. }
  91. private RecipeCategory getCategory(Identifier identifier) {
  92. return categories.stream().filter(category -> category.getIdentifier().equals(identifier)).findFirst().orElse(null);
  93. }
  94. @Override
  95. public RecipeManager getRecipeManager() {
  96. return recipeManager;
  97. }
  98. @Override
  99. public Map<RecipeCategory, List<RecipeDisplay>> getUsagesFor(ItemStack stack) {
  100. Map<Identifier, List<RecipeDisplay>> categoriesMap = new HashMap<>();
  101. categories.forEach(f -> categoriesMap.put(f.getIdentifier(), Lists.newArrayList()));
  102. for(Map.Entry<Identifier, List<RecipeDisplay>> entry : recipeCategoryListMap.entrySet()) {
  103. RecipeCategory category = getCategory(entry.getKey());
  104. for(RecipeDisplay recipeDisplay : entry.getValue()) {
  105. boolean found = false;
  106. for(List<ItemStack> input : (List<List<ItemStack>>) recipeDisplay.getInput()) {
  107. for(ItemStack itemStack : input) {
  108. if (category.checkTags() ? ItemStack.areEqual(itemStack, stack) : ItemStack.areEqualIgnoreTags(itemStack, stack)) {
  109. categoriesMap.get(recipeDisplay.getRecipeCategory()).add(recipeDisplay);
  110. found = true;
  111. break;
  112. }
  113. }
  114. if (found)
  115. break;
  116. }
  117. }
  118. }
  119. Map<RecipeCategory, List<RecipeDisplay>> recipeCategoryListMap = Maps.newLinkedHashMap();
  120. categories.forEach(category -> {
  121. if (categoriesMap.containsKey(category.getIdentifier()) && !categoriesMap.get(category.getIdentifier()).isEmpty())
  122. recipeCategoryListMap.put(category, categoriesMap.get(category.getIdentifier()).stream().filter(display -> isDisplayVisible(display, true)).collect(Collectors.toList()));
  123. });
  124. for(RecipeCategory category : Lists.newArrayList(recipeCategoryListMap.keySet()))
  125. if (recipeCategoryListMap.get(category).isEmpty())
  126. recipeCategoryListMap.remove(category);
  127. return recipeCategoryListMap;
  128. }
  129. @Override
  130. public List<RecipeCategory> getAllCategories() {
  131. return new LinkedList<>(categories);
  132. }
  133. @Override
  134. public Optional<ButtonAreaSupplier> getSpeedCraftButtonArea(RecipeCategory category) {
  135. if (!speedCraftAreaSupplierMap.containsKey(category.getIdentifier()))
  136. return Optional.empty();
  137. return Optional.ofNullable(speedCraftAreaSupplierMap.get(category.getIdentifier()));
  138. }
  139. @Override
  140. public void registerSpeedCraftButtonArea(Identifier category, ButtonAreaSupplier rectangle) {
  141. speedCraftAreaSupplierMap.put(category, rectangle);
  142. }
  143. @Override
  144. public void registerDefaultSpeedCraftButtonArea(Identifier category) {
  145. registerSpeedCraftButtonArea(category, bounds -> new Rectangle((int) bounds.getMaxX() - 16, (int) bounds.getMaxY() - 16, 10, 10));
  146. }
  147. @Override
  148. public List<SpeedCraftFunctional> getSpeedCraftFunctional(RecipeCategory category) {
  149. if (speedCraftFunctionalMap.get(category.getIdentifier()) == null)
  150. return Lists.newArrayList();
  151. return speedCraftFunctionalMap.get(category.getIdentifier());
  152. }
  153. @Override
  154. public void registerSpeedCraftFunctional(Identifier category, SpeedCraftFunctional functional) {
  155. List<SpeedCraftFunctional> list = speedCraftFunctionalMap.containsKey(category) ? new LinkedList<>(speedCraftFunctionalMap.get(category)) : Lists.newLinkedList();
  156. list.add(functional);
  157. speedCraftFunctionalMap.put(category, list);
  158. }
  159. @SuppressWarnings("deprecation")
  160. public void recipesLoaded(RecipeManager recipeManager) {
  161. this.recipeCount.set(0);
  162. this.recipeManager = recipeManager;
  163. this.recipeCategoryListMap.clear();
  164. this.categories.clear();
  165. this.speedCraftAreaSupplierMap.clear();
  166. this.speedCraftFunctionalMap.clear();
  167. this.categoryDisplaySettingsMap.clear();
  168. this.displayVisibilityHandlers.clear();
  169. ((DisplayHelperImpl) RoughlyEnoughItemsCore.getDisplayHelper()).resetCache();
  170. BaseBoundsHandler baseBoundsHandler = new BaseBoundsHandlerImpl();
  171. RoughlyEnoughItemsCore.getDisplayHelper().registerBoundsHandler(baseBoundsHandler);
  172. ((DisplayHelperImpl) RoughlyEnoughItemsCore.getDisplayHelper()).setBaseBoundsHandler(baseBoundsHandler);
  173. long startTime = System.currentTimeMillis();
  174. List<REIPluginEntry> plugins = new LinkedList<>(RoughlyEnoughItemsCore.getPlugins());
  175. plugins.sort((first, second) -> {
  176. return second.getPriority() - first.getPriority();
  177. });
  178. RoughlyEnoughItemsCore.LOGGER.info("[REI] Loading %d plugins: %s", plugins.size(), plugins.stream().map(REIPluginEntry::getPluginIdentifier).map(Identifier::toString).collect(Collectors.joining(", ")));
  179. Collections.reverse(plugins);
  180. RoughlyEnoughItemsCore.getItemRegisterer().getModifiableItemList().clear();
  181. PluginDisabler pluginDisabler = RoughlyEnoughItemsCore.getPluginDisabler();
  182. plugins.forEach(plugin -> {
  183. Identifier identifier = plugin.getPluginIdentifier();
  184. try {
  185. if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_ITEMS))
  186. plugin.registerItems(RoughlyEnoughItemsCore.getItemRegisterer());
  187. if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_CATEGORIES))
  188. plugin.registerPluginCategories(this);
  189. if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_RECIPE_DISPLAYS))
  190. plugin.registerRecipeDisplays(this);
  191. if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_BOUNDS))
  192. plugin.registerBounds(RoughlyEnoughItemsCore.getDisplayHelper());
  193. if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_OTHERS))
  194. plugin.registerOthers(this);
  195. } catch (Exception e) {
  196. RoughlyEnoughItemsCore.LOGGER.error("[REI] %s plugin failed to load: %s", identifier.toString(), e.getLocalizedMessage());
  197. }
  198. });
  199. if (getDisplayVisibilityHandlers().size() == 0)
  200. registerRecipeVisibilityHandler(new DisplayVisibilityHandler() {
  201. @Override
  202. public DisplayVisibility handleDisplay(RecipeCategory category, RecipeDisplay display) {
  203. return DisplayVisibility.ALWAYS_VISIBLE;
  204. }
  205. @Override
  206. public float getPriority() {
  207. return -1f;
  208. }
  209. });
  210. long usedTime = System.currentTimeMillis() - startTime;
  211. RoughlyEnoughItemsCore.LOGGER.info("[REI] Registered %d recipes, %d categories (%s) in %d ms.", recipeCount.get(), categories.size(), String.join(", ", categories.stream().map(RecipeCategory::getCategoryName).collect(Collectors.toList())), usedTime);
  212. }
  213. @Override
  214. public int getRecipeCount() {
  215. return recipeCount.get();
  216. }
  217. @Override
  218. public Map<RecipeCategory, List<RecipeDisplay>> getAllRecipes() {
  219. Map<RecipeCategory, List<RecipeDisplay>> map = Maps.newLinkedHashMap();
  220. Map<Identifier, List<RecipeDisplay>> tempMap = Maps.newLinkedHashMap();
  221. recipeCategoryListMap.forEach((identifier, recipeDisplays) -> tempMap.put(identifier, new LinkedList<>(recipeDisplays)));
  222. categories.forEach(category -> {
  223. if (tempMap.containsKey(category.getIdentifier()))
  224. map.put(category, tempMap.get(category.getIdentifier()).stream().filter(display -> isDisplayVisible(display, true)).collect(Collectors.toList()));
  225. });
  226. for(RecipeCategory category : Lists.newArrayList(map.keySet()))
  227. if (map.get(category).isEmpty())
  228. map.remove(category);
  229. return map;
  230. }
  231. @Override
  232. public void registerRecipeVisibilityHandler(DisplayVisibilityHandler visibilityHandler) {
  233. displayVisibilityHandlers.add(visibilityHandler);
  234. }
  235. @Override
  236. public void unregisterRecipeVisibilityHandler(DisplayVisibilityHandler visibilityHandler) {
  237. displayVisibilityHandlers.remove(visibilityHandler);
  238. }
  239. @Override
  240. public List<DisplayVisibilityHandler> getDisplayVisibilityHandlers() {
  241. return Collections.unmodifiableList(displayVisibilityHandlers);
  242. }
  243. @Override
  244. public boolean isDisplayVisible(RecipeDisplay display, boolean respectConfig) {
  245. RecipeCategory category = getCategory(display.getRecipeCategory());
  246. List<DisplayVisibilityHandler> list = Lists.newArrayList(getDisplayVisibilityHandlers());
  247. list.sort((o1, o2) -> VISIBILITY_HANDLER_COMPARATOR.compare(o1, o2));
  248. for(DisplayVisibilityHandler displayVisibilityHandler : list) {
  249. DisplayVisibility visibility = displayVisibilityHandler.handleDisplay(category, display);
  250. if (visibility != DisplayVisibility.PASS) {
  251. if (visibility == DisplayVisibility.CONFIG_OPTIONAL)
  252. return RoughlyEnoughItemsCore.getConfigManager().getConfig().preferVisibleRecipes || !respectConfig;
  253. return visibility == DisplayVisibility.ALWAYS_VISIBLE;
  254. }
  255. }
  256. return true;
  257. }
  258. @Override
  259. public Optional<DisplaySettings> getCachedCategorySettings(Identifier category) {
  260. return categoryDisplaySettingsMap.entrySet().stream().filter(entry -> entry.getKey().equals(category)).map(Map.Entry::getValue).findAny();
  261. }
  262. }