ItemEntryStack.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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.Lists;
  25. import com.mojang.blaze3d.platform.GlStateManager;
  26. import com.mojang.blaze3d.systems.RenderSystem;
  27. import me.shedaniel.math.Point;
  28. import me.shedaniel.math.Rectangle;
  29. import me.shedaniel.rei.api.ClientHelper;
  30. import me.shedaniel.rei.api.ConfigObject;
  31. import me.shedaniel.rei.api.EntryStack;
  32. import me.shedaniel.rei.api.fractions.Fraction;
  33. import me.shedaniel.rei.api.widgets.Tooltip;
  34. import net.minecraft.client.MinecraftClient;
  35. import net.minecraft.client.item.TooltipContext;
  36. import net.minecraft.client.render.DiffuseLighting;
  37. import net.minecraft.client.render.OverlayTexture;
  38. import net.minecraft.client.render.VertexConsumerProvider;
  39. import net.minecraft.client.render.model.BakedModel;
  40. import net.minecraft.client.render.model.json.ModelTransformation;
  41. import net.minecraft.client.texture.SpriteAtlasTexture;
  42. import net.minecraft.client.util.math.MatrixStack;
  43. import net.minecraft.item.Item;
  44. import net.minecraft.item.ItemStack;
  45. import net.minecraft.nbt.CompoundTag;
  46. import net.minecraft.nbt.ListTag;
  47. import net.minecraft.nbt.Tag;
  48. import net.minecraft.text.LiteralText;
  49. import net.minecraft.text.Text;
  50. import net.minecraft.text.TranslatableText;
  51. import net.minecraft.util.Identifier;
  52. import net.minecraft.util.math.MathHelper;
  53. import net.minecraft.util.registry.Registry;
  54. import org.jetbrains.annotations.ApiStatus;
  55. import org.jetbrains.annotations.NotNull;
  56. import org.jetbrains.annotations.Nullable;
  57. import java.util.List;
  58. import java.util.Map;
  59. import java.util.Optional;
  60. @ApiStatus.Internal
  61. public class ItemEntryStack extends AbstractEntryStack implements OptimalEntryStack {
  62. private ItemStack itemStack;
  63. public ItemEntryStack(ItemStack itemStack) {
  64. this.itemStack = itemStack;
  65. }
  66. @Override
  67. public Optional<Identifier> getIdentifier() {
  68. return Optional.ofNullable(Registry.ITEM.getId(getItem()));
  69. }
  70. @Override
  71. public Type getType() {
  72. return Type.ITEM;
  73. }
  74. @Override
  75. public int getAmount() {
  76. return itemStack.getCount();
  77. }
  78. @Override
  79. public Fraction getAccurateAmount() {
  80. return Fraction.ofWhole(itemStack.getCount());
  81. }
  82. @Override
  83. public void setAmount(Fraction amount) {
  84. itemStack.setCount(amount.intValue());
  85. }
  86. @Override
  87. public void setFloatingAmount(double amount) {
  88. itemStack.setCount(MathHelper.floor(amount));
  89. }
  90. @Override
  91. public boolean isEmpty() {
  92. return itemStack.isEmpty();
  93. }
  94. @Override
  95. public EntryStack copy() {
  96. EntryStack stack = EntryStack.create(getItemStack().copy());
  97. for (Map.Entry<Settings<?>, Object> entry : getSettings().entrySet()) {
  98. stack.setting((Settings<? super Object>) entry.getKey(), entry.getValue());
  99. }
  100. return stack;
  101. }
  102. @Override
  103. public Object getObject() {
  104. return itemStack;
  105. }
  106. /**
  107. * type:
  108. * 0: ignore tags and amount
  109. * 1: ignore tags
  110. * 2: ignore amount
  111. * 3: all
  112. */
  113. private Boolean compareIfFluid(EntryStack stack, int type) {
  114. EntryStack fluid = EntryStack.copyItemToFluid(this);
  115. if (fluid.isEmpty()) return null;
  116. if (stack.getType() == Type.ITEM)
  117. stack = EntryStack.copyItemToFluid(stack);
  118. if (stack.isEmpty()) return null;
  119. switch (type) {
  120. case 0:
  121. return fluid.equalsIgnoreTagsAndAmount(stack);
  122. case 1:
  123. return fluid.equalsIgnoreTags(stack);
  124. case 2:
  125. return fluid.equalsIgnoreAmount(stack);
  126. case 3:
  127. return fluid.equalsAll(stack);
  128. }
  129. return null;
  130. }
  131. @Override
  132. public boolean equalsIgnoreTagsAndAmount(EntryStack stack) {
  133. Boolean ifFluid = compareIfFluid(stack, 0);
  134. if (ifFluid != null) return ifFluid;
  135. if (stack.getType() != Type.ITEM)
  136. return false;
  137. return itemStack.getItem() == stack.getItem();
  138. }
  139. @Override
  140. public boolean equalsAll(EntryStack stack) {
  141. if (stack.getType() != Type.ITEM)
  142. return false;
  143. return itemStack.getItem() == stack.getItem() && getAmount() != stack.getAmount() && ItemStack.areTagsEqual(itemStack, stack.getItemStack());
  144. }
  145. @Override
  146. public boolean equalsIgnoreAmount(EntryStack stack) {
  147. Boolean ifFluid = compareIfFluid(stack, 2);
  148. if (ifFluid != null) return ifFluid;
  149. if (stack.getType() != Type.ITEM)
  150. return false;
  151. if (itemStack.getItem() != stack.getItem())
  152. return false;
  153. ItemStack otherStack = stack.getItemStack();
  154. CompoundTag o1 = itemStack.getTag();
  155. CompoundTag o2 = otherStack.getTag();
  156. return o1 == o2 || ((o1 != null && o2 != null) && equalsTagWithoutCount(o1, o2));
  157. }
  158. private boolean equalsTagWithoutCount(CompoundTag o1, CompoundTag o2) {
  159. int o1Size = 0;
  160. int o2Size = 0;
  161. for (String key : o1.getKeys()) {
  162. if (key.equals("Count"))
  163. continue;
  164. o1Size++;
  165. }
  166. for (String key : o2.getKeys()) {
  167. if (key.equals("Count"))
  168. continue;
  169. o2Size++;
  170. if (o2Size > o1Size)
  171. return false;
  172. }
  173. if (o1Size != o2Size)
  174. return false;
  175. try {
  176. for (String key : o1.getKeys()) {
  177. if (key.equals("Count"))
  178. continue;
  179. Tag value = o1.get(key);
  180. Tag otherValue = o2.get(key);
  181. if (!equalsTag(value, otherValue))
  182. return false;
  183. }
  184. } catch (ClassCastException | NullPointerException unused) {
  185. return false;
  186. }
  187. return true;
  188. }
  189. private boolean equalsTag(Tag tag, Tag otherTag) {
  190. if (tag == null || otherTag == null) {
  191. return tag == otherTag;
  192. }
  193. if (tag instanceof ListTag && otherTag instanceof ListTag)
  194. return equalsList((ListTag) tag, (ListTag) otherTag);
  195. return tag.equals(otherTag);
  196. }
  197. private boolean equalsList(ListTag listTag, ListTag otherListTag) {
  198. if (listTag.size() != otherListTag.size())
  199. return false;
  200. for (int i = 0; i < listTag.size(); i++) {
  201. Tag value = listTag.get(i);
  202. Tag otherValue = otherListTag.get(i);
  203. if (!equalsTag(value, otherValue))
  204. return false;
  205. }
  206. return true;
  207. }
  208. @Override
  209. public boolean equalsIgnoreTags(EntryStack stack) {
  210. Boolean ifFluid = compareIfFluid(stack, 1);
  211. if (ifFluid != null) return ifFluid;
  212. if (stack.getType() != Type.ITEM)
  213. return false;
  214. if (itemStack.getItem() != stack.getItem())
  215. return false;
  216. return getAmount() == stack.getAmount();
  217. }
  218. @Override
  219. public int hashOfAll() {
  220. int result = hashIgnoreAmount();
  221. result = 31 * result + itemStack.getCount();
  222. return result;
  223. }
  224. @Override
  225. public int hashIgnoreTags() {
  226. int result = hashIgnoreAmountAndTags();
  227. result = 31 * result + itemStack.getCount();
  228. return result;
  229. }
  230. @Override
  231. public int hashIgnoreAmount() {
  232. int result = hashIgnoreAmountAndTags();
  233. if (itemStack.hasTag()) {
  234. result = 31 * result + itemStack.getTag().toString().hashCode();
  235. } else {
  236. result = 31 * result;
  237. }
  238. return result;
  239. }
  240. @Override
  241. public int hashIgnoreAmountAndTags() {
  242. int result = 1;
  243. result = 31 * result + itemStack.getItem().hashCode();
  244. return result;
  245. }
  246. @Nullable
  247. @Override
  248. public Tooltip getTooltip(Point point) {
  249. if (isEmpty() || !get(Settings.TOOLTIP_ENABLED).get())
  250. return null;
  251. List<Text> toolTip = tryGetItemStackToolTip(true);
  252. toolTip.addAll(get(Settings.TOOLTIP_APPEND_EXTRA).apply(this));
  253. if (get(Settings.TOOLTIP_APPEND_MOD).get() && ConfigObject.getInstance().shouldAppendModNames()) {
  254. final String modId = ClientHelper.getInstance().getModFromItem(getItem());
  255. boolean alreadyHasMod = false;
  256. for (Text s : toolTip)
  257. if (s.getString().equalsIgnoreCase(modId)) {
  258. alreadyHasMod = true;
  259. break;
  260. }
  261. if (!alreadyHasMod)
  262. toolTip.add(ClientHelper.getInstance().getFormattedModFromItem(getItem()));
  263. }
  264. return Tooltip.create(toolTip);
  265. }
  266. @Override
  267. public void render(MatrixStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
  268. optimisedRenderStart(matrices, delta);
  269. VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
  270. optimisedRenderBase(matrices, immediate, bounds, mouseX, mouseY, delta);
  271. immediate.draw();
  272. optimisedRenderOverlay(matrices, bounds, mouseX, mouseY, delta);
  273. optimisedRenderEnd(matrices, delta);
  274. }
  275. @Override
  276. public void optimisedRenderStart(MatrixStack matrices, float delta) {
  277. optimisedRenderStart(matrices, delta, true);
  278. }
  279. @SuppressWarnings("deprecation")
  280. public void optimisedRenderStart(MatrixStack matrices, float delta, boolean isOptimised) {
  281. MinecraftClient.getInstance().getTextureManager().bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX);
  282. MinecraftClient.getInstance().getTextureManager().getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX).setFilter(false, false);
  283. RenderSystem.pushMatrix();
  284. RenderSystem.enableRescaleNormal();
  285. RenderSystem.enableAlphaTest();
  286. RenderSystem.defaultAlphaFunc();
  287. RenderSystem.enableBlend();
  288. RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
  289. RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
  290. if (isOptimised) {
  291. boolean sideLit = getModelFromStack(itemStack).isSideLit();
  292. if (!sideLit)
  293. DiffuseLighting.disableGuiDepthLighting();
  294. }
  295. }
  296. @Override
  297. public void optimisedRenderEnd(MatrixStack matrices, float delta) {
  298. optimisedRenderEnd(matrices, delta, true);
  299. }
  300. @SuppressWarnings("deprecation")
  301. public void optimisedRenderEnd(MatrixStack matrices, float delta, boolean isOptimised) {
  302. RenderSystem.enableDepthTest();
  303. RenderSystem.disableAlphaTest();
  304. RenderSystem.disableRescaleNormal();
  305. if (isOptimised) {
  306. boolean sideLit = getModelFromStack(itemStack).isSideLit();
  307. if (!sideLit)
  308. DiffuseLighting.enableGuiDepthLighting();
  309. }
  310. RenderSystem.popMatrix();
  311. }
  312. private BakedModel getModelFromStack(ItemStack stack) {
  313. return MinecraftClient.getInstance().getItemRenderer().getHeldItemModel(stack, null, null);
  314. }
  315. @Override
  316. public int groupingHash() {
  317. return 1738923 + (getModelFromStack(itemStack).isSideLit() ? 1 : 0);
  318. }
  319. @Override
  320. public void optimisedRenderBase(MatrixStack matrices, VertexConsumerProvider.Immediate immediate, Rectangle bounds, int mouseX, int mouseY, float delta) {
  321. if (!isEmpty() && get(Settings.RENDER).get()) {
  322. ItemStack stack = getItemStack();
  323. ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(get(Settings.Item.RENDER_ENCHANTMENT_GLINT).get());
  324. matrices.push();
  325. matrices.translate(bounds.getCenterX(), bounds.getCenterY(), 100.0F + getZ());
  326. matrices.scale(bounds.getWidth(), (bounds.getWidth() + bounds.getHeight()) / -2f, bounds.getHeight());
  327. MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Mode.GUI, false, matrices, immediate, 15728880, OverlayTexture.DEFAULT_UV, getModelFromStack(stack));
  328. matrices.pop();
  329. ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(false);
  330. }
  331. }
  332. @Override
  333. public void optimisedRenderOverlay(MatrixStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
  334. if (!isEmpty() && get(Settings.RENDER).get()) {
  335. MinecraftClient.getInstance().getItemRenderer().zOffset = getZ();
  336. MinecraftClient.getInstance().getItemRenderer().renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, getItemStack(), bounds.x, bounds.y, get(Settings.RENDER_COUNTS).get() ? get(Settings.COUNTS).apply(this) : "");
  337. MinecraftClient.getInstance().getItemRenderer().zOffset = 0.0F;
  338. }
  339. }
  340. private static final List<Item> SEARCH_BLACKLISTED = Lists.newArrayList();
  341. @Override
  342. public @NotNull Text asFormattedText() {
  343. if (!SEARCH_BLACKLISTED.contains(getItem()))
  344. try {
  345. return getItemStack().getName();
  346. } catch (Throwable e) {
  347. e.printStackTrace();
  348. SEARCH_BLACKLISTED.add(getItem());
  349. }
  350. try {
  351. return new TranslatableText("item." + Registry.ITEM.getId(getItem()).toString().replace(":", "."));
  352. } catch (Throwable e) {
  353. e.printStackTrace();
  354. }
  355. return new LiteralText("ERROR");
  356. }
  357. private List<Text> tryGetItemStackToolTip(boolean careAboutAdvanced) {
  358. if (!SEARCH_BLACKLISTED.contains(getItem()))
  359. try {
  360. return itemStack.getTooltip(MinecraftClient.getInstance().player, MinecraftClient.getInstance().options.advancedItemTooltips && careAboutAdvanced ? TooltipContext.Default.ADVANCED : TooltipContext.Default.NORMAL);
  361. } catch (Throwable e) {
  362. e.printStackTrace();
  363. SEARCH_BLACKLISTED.add(getItem());
  364. }
  365. return Lists.newArrayList(asFormattedText());
  366. }
  367. }