ClientHelperImpl.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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.impl;
  24. import com.google.common.collect.Maps;
  25. import io.netty.buffer.Unpooled;
  26. import me.shedaniel.rei.RoughlyEnoughItemsCore;
  27. import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
  28. import me.shedaniel.rei.api.*;
  29. import me.shedaniel.rei.gui.PreRecipeViewingScreen;
  30. import me.shedaniel.rei.gui.RecipeScreen;
  31. import me.shedaniel.rei.gui.RecipeViewingScreen;
  32. import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
  33. import me.shedaniel.rei.gui.config.RecipeScreenType;
  34. import net.fabricmc.api.ClientModInitializer;
  35. import net.fabricmc.api.EnvType;
  36. import net.fabricmc.api.Environment;
  37. import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
  38. import net.fabricmc.loader.api.FabricLoader;
  39. import net.fabricmc.loader.api.ModContainer;
  40. import net.fabricmc.loader.api.metadata.ModMetadata;
  41. import net.minecraft.client.MinecraftClient;
  42. import net.minecraft.client.gui.screen.Screen;
  43. import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
  44. import net.minecraft.client.util.NarratorManager;
  45. import net.minecraft.entity.player.PlayerInventory;
  46. import net.minecraft.item.Item;
  47. import net.minecraft.item.ItemStack;
  48. import net.minecraft.item.Items;
  49. import net.minecraft.network.PacketByteBuf;
  50. import net.minecraft.text.LiteralText;
  51. import net.minecraft.text.Text;
  52. import net.minecraft.text.TranslatableText;
  53. import net.minecraft.util.Formatting;
  54. import net.minecraft.util.Identifier;
  55. import net.minecraft.util.Lazy;
  56. import net.minecraft.util.math.MathHelper;
  57. import net.minecraft.util.registry.Registry;
  58. import org.jetbrains.annotations.ApiStatus;
  59. import org.jetbrains.annotations.NotNull;
  60. import org.jetbrains.annotations.Nullable;
  61. import java.time.LocalDateTime;
  62. import java.util.*;
  63. @ApiStatus.Internal
  64. @Environment(EnvType.CLIENT)
  65. public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
  66. private static ClientHelperImpl instance;
  67. @ApiStatus.Internal public final Lazy<Boolean> isYog = new Lazy<>(() -> {
  68. try {
  69. if (MinecraftClient.getInstance().getSession().getProfile().getId().equals(UUID.fromString("f9546389-9415-4358-9c29-2c26b25bff5b")))
  70. return true;
  71. } catch (Throwable ignored) {
  72. }
  73. return false;
  74. });
  75. @ApiStatus.Internal public final Lazy<Boolean> isAprilFools = new Lazy<>(() -> {
  76. try {
  77. LocalDateTime now = LocalDateTime.now();
  78. return now.getMonthValue() == 4 && now.getDayOfMonth() == 1;
  79. } catch (Throwable ignored) {
  80. }
  81. return false;
  82. });
  83. private final Map<String, String> modNameCache = Maps.newHashMap();
  84. /**
  85. * @return the instance of {@link ClientHelperImpl}
  86. * @see ClientHelper#getInstance()
  87. */
  88. @ApiStatus.Internal
  89. public static ClientHelperImpl getInstance() {
  90. return instance;
  91. }
  92. @Override
  93. public Text getFormattedModFromItem(Item item) {
  94. String mod = getModFromItem(item);
  95. if (mod.isEmpty())
  96. return NarratorManager.EMPTY;
  97. return new LiteralText(mod).formatted(Formatting.BLUE, Formatting.ITALIC);
  98. }
  99. @Override
  100. public Text getFormattedModFromIdentifier(Identifier identifier) {
  101. String mod = getModFromIdentifier(identifier);
  102. if (mod.isEmpty())
  103. return NarratorManager.EMPTY;
  104. return new LiteralText(mod).formatted(Formatting.BLUE, Formatting.ITALIC);
  105. }
  106. @Override
  107. public String getModFromItem(Item item) {
  108. if (item.equals(Items.AIR))
  109. return "";
  110. return getModFromIdentifier(Registry.ITEM.getId(item));
  111. }
  112. @Override
  113. public String getModFromModId(String modid) {
  114. if (modid == null)
  115. return "";
  116. String any = modNameCache.getOrDefault(modid, null);
  117. if (any != null)
  118. return any;
  119. String s = FabricLoader.getInstance().getModContainer(modid).map(ModContainer::getMetadata).map(ModMetadata::getName).orElse(modid);
  120. modNameCache.put(modid, s);
  121. return s;
  122. }
  123. @Override
  124. public boolean isCheating() {
  125. return ConfigObject.getInstance().isCheating();
  126. }
  127. @Override
  128. public void setCheating(boolean cheating) {
  129. ConfigObject.getInstance().setCheating(cheating);
  130. ConfigManager.getInstance().saveConfig();
  131. }
  132. @Override
  133. public void sendDeletePacket() {
  134. if (MinecraftClient.getInstance().currentScreen instanceof CreativeInventoryScreen) {
  135. MinecraftClient.getInstance().player.inventory.setCursorStack(ItemStack.EMPTY);
  136. ((CreativeInventoryScreen) MinecraftClient.getInstance().currentScreen).isCursorDragging = false;
  137. return;
  138. }
  139. ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.DELETE_ITEMS_PACKET, new PacketByteBuf(Unpooled.buffer()));
  140. }
  141. @Override
  142. public boolean tryCheatingEntry(EntryStack entry) {
  143. if (entry.getType() != EntryStack.Type.ITEM)
  144. return false;
  145. if (MinecraftClient.getInstance().player == null) return false;
  146. if (MinecraftClient.getInstance().player.inventory == null) return false;
  147. ItemStack cheatedStack = entry.getItemStack().copy();
  148. if (ConfigObject.getInstance().isGrabbingItems() && MinecraftClient.getInstance().currentScreen instanceof CreativeInventoryScreen) {
  149. PlayerInventory inventory = MinecraftClient.getInstance().player.inventory;
  150. EntryStack stack = entry.copy();
  151. if (!inventory.getCursorStack().isEmpty() && EntryStack.create(inventory.getCursorStack()).equalsIgnoreAmount(stack)) {
  152. stack.setAmount(MathHelper.clamp(stack.getAmount() + inventory.getCursorStack().getCount(), 1, stack.getItemStack().getMaxCount()));
  153. } else if (!inventory.getCursorStack().isEmpty()) {
  154. return false;
  155. }
  156. inventory.setCursorStack(stack.getItemStack().copy());
  157. return true;
  158. } else if (RoughlyEnoughItemsCore.canUsePackets()) {
  159. try {
  160. ClientSidePacketRegistry.INSTANCE.sendToServer(ConfigObject.getInstance().isGrabbingItems() ? RoughlyEnoughItemsNetwork.CREATE_ITEMS_GRAB_PACKET : RoughlyEnoughItemsNetwork.CREATE_ITEMS_PACKET, new PacketByteBuf(Unpooled.buffer()).writeItemStack(cheatedStack));
  161. return true;
  162. } catch (Exception e) {
  163. return false;
  164. }
  165. } else {
  166. Identifier identifier = entry.getIdentifier().orElse(null);
  167. if (identifier == null)
  168. return false;
  169. String tagMessage = cheatedStack.copy().getTag() != null && !cheatedStack.copy().getTag().isEmpty() ? cheatedStack.copy().getTag().asString() : "";
  170. String og = cheatedStack.getCount() == 1 ? ConfigObject.getInstance().getGiveCommand().replaceAll(" \\{count}", "") : ConfigObject.getInstance().getGiveCommand();
  171. String madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_name}", identifier.getPath()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", tagMessage).replaceAll("\\{count}", String.valueOf(cheatedStack.getCount()));
  172. if (madeUpCommand.length() > 256) {
  173. madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_name}", identifier.getPath()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", "").replaceAll("\\{count}", String.valueOf(cheatedStack.getCount()));
  174. MinecraftClient.getInstance().player.addMessage(new TranslatableText("text.rei.too_long_nbt"), false);
  175. }
  176. MinecraftClient.getInstance().player.sendChatMessage(madeUpCommand);
  177. return true;
  178. }
  179. }
  180. @Override
  181. public List<ItemStack> getInventoryItemsTypes() {
  182. List<ItemStack> inventoryStacks = new ArrayList<>(MinecraftClient.getInstance().player.inventory.main);
  183. inventoryStacks.addAll(MinecraftClient.getInstance().player.inventory.armor);
  184. inventoryStacks.addAll(MinecraftClient.getInstance().player.inventory.offHand);
  185. return inventoryStacks;
  186. }
  187. @ApiStatus.ScheduledForRemoval
  188. @Deprecated
  189. @Override
  190. public void openRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
  191. openRecipeViewingScreen(map, null, null, null);
  192. }
  193. @ApiStatus.ScheduledForRemoval
  194. @Deprecated
  195. @ApiStatus.Internal
  196. public void openRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map, @Nullable Identifier category, @Nullable EntryStack ingredientNotice, @Nullable EntryStack resultNotice) {
  197. openView(new LegacyWrapperViewSearchBuilder(map).setPreferredOpenedCategory(category).setInputNotice(ingredientNotice).setOutputNotice(resultNotice).fillPreferredOpenedCategory());
  198. }
  199. @Override
  200. public boolean openView(ClientHelper.ViewSearchBuilder builder) {
  201. Map<RecipeCategory<?>, List<RecipeDisplay>> map = builder.buildMap();
  202. if (map.isEmpty()) return false;
  203. Screen screen;
  204. if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.VILLAGER) {
  205. screen = new VillagerRecipeViewingScreen(map, builder.getPreferredOpenedCategory());
  206. } else if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.UNSET) {
  207. screen = new PreRecipeViewingScreen(REIHelper.getInstance().getPreviousContainerScreen(), RecipeScreenType.UNSET, true, original -> {
  208. ConfigObject.getInstance().setRecipeScreenType(original ? RecipeScreenType.ORIGINAL : RecipeScreenType.VILLAGER);
  209. ConfigManager.getInstance().saveConfig();
  210. openView(builder);
  211. });
  212. } else {
  213. screen = new RecipeViewingScreen(map, builder.getPreferredOpenedCategory());
  214. }
  215. if (screen instanceof RecipeScreen) {
  216. if (builder.getInputNotice() != null)
  217. ((RecipeScreen) screen).addIngredientStackToNotice(builder.getInputNotice());
  218. if (builder.getOutputNotice() != null)
  219. ((RecipeScreen) screen).addResultStackToNotice(builder.getOutputNotice());
  220. }
  221. if (MinecraftClient.getInstance().currentScreen instanceof RecipeScreen)
  222. ScreenHelper.storeRecipeScreen((RecipeScreen) MinecraftClient.getInstance().currentScreen);
  223. MinecraftClient.getInstance().openScreen(screen);
  224. return true;
  225. }
  226. @Override
  227. public void onInitializeClient() {
  228. ClientHelperImpl.instance = this;
  229. modNameCache.put("minecraft", "Minecraft");
  230. modNameCache.put("c", "Global");
  231. modNameCache.put("global", "Global");
  232. }
  233. public static final class ViewSearchBuilder implements ClientHelper.ViewSearchBuilder {
  234. @NotNull private final Set<Identifier> categories = new HashSet<>();
  235. @NotNull private final List<EntryStack> recipesFor = new ArrayList<>();
  236. @NotNull private final List<EntryStack> usagesFor = new ArrayList<>();
  237. @Nullable private Identifier preferredOpenedCategory = null;
  238. @Nullable private EntryStack inputNotice;
  239. @Nullable private EntryStack outputNotice;
  240. @NotNull private final Lazy<Map<RecipeCategory<?>, List<RecipeDisplay>>> map = new Lazy<>(() -> RecipeHelper.getInstance().buildMapFor(this));
  241. @Override
  242. public ClientHelper.ViewSearchBuilder addCategory(Identifier category) {
  243. this.categories.add(category);
  244. return this;
  245. }
  246. @Override
  247. public ClientHelper.ViewSearchBuilder addCategories(Collection<Identifier> categories) {
  248. this.categories.addAll(categories);
  249. return this;
  250. }
  251. @Override
  252. @NotNull
  253. public Set<Identifier> getCategories() {
  254. return categories;
  255. }
  256. @Override
  257. public ClientHelper.ViewSearchBuilder addRecipesFor(EntryStack stack) {
  258. this.recipesFor.add(stack);
  259. return this;
  260. }
  261. @Override
  262. @NotNull
  263. public List<EntryStack> getRecipesFor() {
  264. return recipesFor;
  265. }
  266. @Override
  267. public ClientHelper.ViewSearchBuilder addUsagesFor(EntryStack stack) {
  268. this.usagesFor.add(stack);
  269. return this;
  270. }
  271. @Override
  272. @NotNull
  273. public List<EntryStack> getUsagesFor() {
  274. return usagesFor;
  275. }
  276. @Override
  277. public ClientHelper.ViewSearchBuilder setPreferredOpenedCategory(@Nullable Identifier category) {
  278. this.preferredOpenedCategory = category;
  279. return this;
  280. }
  281. @Nullable
  282. @Override
  283. public Identifier getPreferredOpenedCategory() {
  284. return this.preferredOpenedCategory;
  285. }
  286. @Override
  287. public ClientHelper.ViewSearchBuilder setInputNotice(@Nullable EntryStack stack) {
  288. this.inputNotice = stack;
  289. return this;
  290. }
  291. @Nullable
  292. @Override
  293. public EntryStack getInputNotice() {
  294. return inputNotice;
  295. }
  296. @Override
  297. public ClientHelper.ViewSearchBuilder setOutputNotice(@Nullable EntryStack stack) {
  298. this.outputNotice = stack;
  299. return this;
  300. }
  301. @Nullable
  302. @Override
  303. public EntryStack getOutputNotice() {
  304. return outputNotice;
  305. }
  306. @NotNull
  307. @Override
  308. public Map<RecipeCategory<?>, List<RecipeDisplay>> buildMap() {
  309. return this.map.get();
  310. }
  311. }
  312. public static final class LegacyWrapperViewSearchBuilder implements ClientHelper.ViewSearchBuilder {
  313. @NotNull private final Map<RecipeCategory<?>, List<RecipeDisplay>> map;
  314. @Nullable private Identifier preferredOpenedCategory = null;
  315. @Nullable private EntryStack inputNotice;
  316. @Nullable private EntryStack outputNotice;
  317. public LegacyWrapperViewSearchBuilder(@NotNull Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
  318. this.map = map;
  319. }
  320. @Override
  321. public ClientHelper.ViewSearchBuilder addCategory(Identifier category) {
  322. return this;
  323. }
  324. @Override
  325. public ClientHelper.ViewSearchBuilder addCategories(Collection<Identifier> categories) {
  326. return this;
  327. }
  328. @Override
  329. public @NotNull Set<Identifier> getCategories() {
  330. return Collections.emptySet();
  331. }
  332. @Override
  333. public ClientHelper.ViewSearchBuilder addRecipesFor(EntryStack stack) {
  334. return this;
  335. }
  336. @Override
  337. public @NotNull List<EntryStack> getRecipesFor() {
  338. return Collections.emptyList();
  339. }
  340. @Override
  341. public ClientHelper.ViewSearchBuilder addUsagesFor(EntryStack stack) {
  342. return this;
  343. }
  344. @Override
  345. public @NotNull List<EntryStack> getUsagesFor() {
  346. return Collections.emptyList();
  347. }
  348. @Override
  349. public ClientHelper.ViewSearchBuilder setPreferredOpenedCategory(@Nullable Identifier category) {
  350. this.preferredOpenedCategory = category;
  351. return this;
  352. }
  353. @Nullable
  354. @Override
  355. public Identifier getPreferredOpenedCategory() {
  356. return this.preferredOpenedCategory;
  357. }
  358. @Override
  359. public ClientHelper.ViewSearchBuilder setInputNotice(@Nullable EntryStack stack) {
  360. this.inputNotice = stack;
  361. return this;
  362. }
  363. @Nullable
  364. @Override
  365. public EntryStack getInputNotice() {
  366. return inputNotice;
  367. }
  368. @Override
  369. public ClientHelper.ViewSearchBuilder setOutputNotice(@Nullable EntryStack stack) {
  370. this.outputNotice = stack;
  371. return this;
  372. }
  373. @Nullable
  374. @Override
  375. public EntryStack getOutputNotice() {
  376. return outputNotice;
  377. }
  378. @Override
  379. public @NotNull Map<RecipeCategory<?>, List<RecipeDisplay>> buildMap() {
  380. return this.map;
  381. }
  382. }
  383. }