55import net .minecraft .entity .effect .StatusEffectInstance ;
66import net .minecraft .entity .effect .StatusEffects ;
77import net .minecraft .item .*;
8- import net .minecraft .nbt .NbtCompound ;
9- import net .minecraft .nbt .NbtElement ;
10- import net .minecraft .nbt .NbtList ;
8+ import net .minecraft .nbt .*;
119import net .minecraft .potion .PotionUtil ;
1210import net .minecraft .util .Identifier ;
1311import net .minecraft .util .registry .Registry ;
@@ -34,11 +32,11 @@ public interface ItemStackChecker {
3432 /**
3533 * Creates a fake ItemStack based on the
3634 * provided stack.
37- *
35+ * <p>
3836 * E.g. removes enchantment info, stack size, etc.
3937 * Used on ground ItemEntities / opponent's stacks etc.
4038 *
41- * @param original the original ItemStack
39+ * @param original the original ItemStack
4240 * @param spoofCount whether or not the item count should be faked
4341 * @return the faked ItemStack
4442 */
@@ -47,52 +45,78 @@ static ItemStack fakeStack(ItemStack original, boolean spoofCount) {
4745 new ItemStack (original .getItem (), original .getMaxCount ()) :
4846 new ItemStack (original .getItem (), original .getCount ());
4947
50- if (original .hasEnchantments ())
48+ if (original .hasEnchantments ())
5149 fakedStack .addEnchantment (null , 0 );
5250
5351 Item item = original .getItem ();
54- if (item instanceof DyeableItem dyeable ) {
55- if (dyeable .hasColor (original )) {
52+ if (item instanceof DyeableItem dyeable ) {
53+ if (dyeable .hasColor (original )) {
5654 dyeable .setColor (fakedStack , dyeable .getColor (original ));
5755 }
5856 }
5957
60- if (item instanceof PotionItem || item instanceof TippedArrowItem ) {
58+ if (item instanceof PotionItem || item instanceof TippedArrowItem ) {
6159 // Lets dropping potions and arrows to be less of a 'surprising' change.
62- fakedStack .getOrCreateNbt (). putInt ( "CustomPotionColor" , PotionUtil .getColor (original ));
60+ fakedStack .setSubNbt ( PotionUtil . CUSTOM_POTION_COLOR_KEY , NbtInt . of ( PotionUtil .getColor (original ) ));
6361 if (item .hasGlint (original )) {
6462 PotionUtil .setCustomPotionEffects (fakedStack , UNLUCK );
6563 }
6664 }
6765
68- if (item instanceof WritableBookItem || item instanceof WrittenBookItem ) {
66+ if (item instanceof WritableBookItem || item instanceof WrittenBookItem ) {
6967 // Prevents issues with other mods expecting pages to be present.
7068 fakedStack .setSubNbt ("pages" , new NbtList ());
7169 }
7270
73- if (item instanceof BannerItem ) {
71+ if (item instanceof BannerItem ) {
7472 // Lets dropping banners not be a surprising change, and for illager patrol leaders to have a proper banner.
75- cleanBanner (original .getSubNbt ("BlockEntityTag" ), fakedStack );
73+ cleanBanner (original .getSubNbt (BlockItem .BLOCK_STATE_TAG_KEY ), fakedStack );
74+ }
75+
76+ if (item instanceof SkullItem ) {
77+ // Lets dropping player heads not be a surprising change, and for wearing heads to show the actual skin.
78+ cleanSkull (original .getSubNbt (SkullItem .SKULL_OWNER_KEY ), fakedStack );
79+ }
80+
81+ if (item instanceof CrossbowItem && CrossbowItem .isCharged (original )) {
82+ cleanCrossbow (original .getNbt (), fakedStack , false );
7683 }
7784
7885 return fakedStack ;
7986 }
8087
88+ /**
89+ * This is for keeping creative from getting inadvertently sanitised,
90+ * as creative has the property of allowing one to summon any item at will,
91+ * including freely mutating the inventory.
92+ *
93+ * @param stack The original ItemStack.
94+ * @return The faked ItemStack, with a GolfIV pointer injected.
95+ * @author KJP12
96+ * @see #inventoryStack(ItemStack)
97+ */
98+ static ItemStack creativeInventoryStack (ItemStack stack ) {
99+ ItemStack fake = inventoryStack (stack );
100+ if (stack .hasNbt ()) //noinspection ConstantConditions
101+ fake .setSubNbt ("GolfIV" , NbtInt .of (stack .getNbt ().hashCode ()));
102+ return fake ;
103+ }
104+
81105 /**
82106 * Creates a fake ItemStack based on the
83107 * provided stack.
84- *
108+ * <p>
85109 * Changes the tag to be the minimal required
86110 * NBT to render in an inventory.
87111 *
88112 * @param stack The original ItemStack.
89113 * @return The faked ItemStack
90- * * /
114+ */
91115 static ItemStack inventoryStack (ItemStack stack ) {
92116 // TODO: Perhaps take a more dynamic approach to this?
93117 // This is not really flexible as is and may leave modded items broken.
94118 NbtCompound tag = stack .getNbt ();
95- if (tag == null || tag .isEmpty ()) {
119+ if (tag == null || tag .isEmpty ()) {
96120 // Sanitization isn't necessary when it's already empty.
97121 return stack ;
98122 }
@@ -101,14 +125,15 @@ static ItemStack inventoryStack(ItemStack stack) {
101125 ItemStack fake = new ItemStack (item , stack .getCount ());
102126
103127 // Rewrite display.
104- if (tag .contains (ItemStack .DISPLAY_KEY )) {
128+ if (tag .contains (ItemStack .DISPLAY_KEY )) {
105129 NbtCompound display = tag .getCompound (ItemStack .DISPLAY_KEY );
106130 NbtCompound fakeDisplay = new NbtCompound ();
107131 NbtElement name = display .get (ItemStack .NAME_KEY );
108- if (name != null ) fakeDisplay .put (ItemStack .NAME_KEY , name );
109- if (display .contains (ItemStack .COLOR_KEY )) fakeDisplay .put (ItemStack .COLOR_KEY , display .get (ItemStack .COLOR_KEY ));
132+ if (name != null ) fakeDisplay .put (ItemStack .NAME_KEY , name );
133+ if (display .contains (ItemStack .COLOR_KEY ))
134+ fakeDisplay .put (ItemStack .COLOR_KEY , display .get (ItemStack .COLOR_KEY ));
110135 NbtElement lore = display .get (ItemStack .LORE_KEY );
111- if (lore != null ) fakeDisplay .put (ItemStack .LORE_KEY , lore );
136+ if (lore != null ) fakeDisplay .put (ItemStack .LORE_KEY , lore );
112137 fake .setSubNbt (ItemStack .DISPLAY_KEY , fakeDisplay );
113138 }
114139
@@ -140,31 +165,40 @@ static ItemStack inventoryStack(ItemStack stack) {
140165 // Check block items.
141166 if (item instanceof BlockItem ) {
142167 boolean flag = true ;
143- if (item instanceof BannerItem ) {
168+ if (item instanceof BannerItem ) {
144169 flag = false ;
145- cleanBanner (stack .getSubNbt ("BlockEntityTag" ), fake );
170+ cleanBanner (stack .getSubNbt (BlockItem . BLOCK_STATE_TAG_KEY ), fake );
146171 }
147172
148- if (flag ) {
173+ // Transfer SkullProperties.
174+ if (item instanceof SkullItem ) {
175+ flag = false ;
176+ cleanSkull (stack .getSubNbt (SkullItem .SKULL_OWNER_KEY ), fake );
177+ }
178+
179+ if (flag ) {
149180 Block block = ((BlockItem ) item ).getBlock ();
150181
151182 // Rewrite shulker items
152183 if (block instanceof ShulkerBoxBlock ) {
153- NbtCompound blockEntity = stack .getSubNbt ("BlockEntityTag" );
154- if (blockEntity != null ) {
155- NbtCompound fakeEntity = fake .getOrCreateSubNbt ("BlockEntityTag" );
156- if (blockEntity .contains ("LootTable" , NbtElement .STRING_TYPE )) {
184+ NbtCompound blockEntity = stack .getSubNbt (BlockItem . BLOCK_STATE_TAG_KEY );
185+ if (blockEntity != null ) {
186+ NbtCompound fakeEntity = fake .getOrCreateSubNbt (BlockItem . BLOCK_STATE_TAG_KEY );
187+ if (blockEntity .contains ("LootTable" , NbtElement .STRING_TYPE )) {
157188 fakeEntity .put ("LootTable" , blockEntity .get ("LootTable" ));
158189 }
159- if (blockEntity .contains ("Items" , NbtElement .LIST_TYPE )) {
190+ if (blockEntity .contains ("Items" , NbtElement .LIST_TYPE )) {
160191 NbtList fakeItems = new NbtList ();
161- for (NbtElement $item : blockEntity .getList ("Items" , NbtElement .COMPOUND_TYPE )) {
162- if ($item == null ) continue ;
192+ for (NbtElement $item : blockEntity .getList ("Items" , NbtElement .COMPOUND_TYPE )) {
193+ if ($item == null ) continue ;
163194 NbtCompound oldItem = (NbtCompound ) $item ;
164195 NbtCompound fakeItem = new NbtCompound ();
165- if (oldItem .contains ("Slot" , NbtElement .BYTE_TYPE )) fakeItem .put ("Slot" , oldItem .get ("Slot" ));
166- if (oldItem .contains ("id" , NbtElement .STRING_TYPE )) fakeItem .put ("id" , oldItem .get ("id" ));
167- if (oldItem .contains ("Count" , NbtElement .BYTE_TYPE )) fakeItem .put ("Count" , oldItem .get ("Count" ));
196+ if (oldItem .contains ("Slot" , NbtElement .BYTE_TYPE ))
197+ fakeItem .put ("Slot" , oldItem .get ("Slot" ));
198+ if (oldItem .contains ("id" , NbtElement .STRING_TYPE ))
199+ fakeItem .put ("id" , oldItem .get ("id" ));
200+ if (oldItem .contains ("Count" , NbtElement .BYTE_TYPE ))
201+ fakeItem .put ("Count" , oldItem .get ("Count" ));
168202 // TODO: Add in display name
169203 fakeItems .add (fakeItem );
170204 }
@@ -204,27 +238,94 @@ static ItemStack inventoryStack(ItemStack stack) {
204238 fake .setSubNbt ("pages" , new NbtList ());
205239 }
206240
207- if (item instanceof WritableBookItem ) {
241+ if (item instanceof WritableBookItem ) {
208242 // FIXME: Pages need to be present for the book to work. Force update on selection?
209243 // Prevents issues with other mods expecting pages to be present.
210244 fake .setSubNbt ("pages" , new NbtList ());
211245 }
212246
213- if (item instanceof FilledMapItem ) {
247+ if (item instanceof FilledMapItem ) {
214248 fake .getOrCreateNbt ().putInt ("map" , tag .getInt ("map" ));
215249 }
216250
251+ if (item instanceof CrossbowItem && CrossbowItem .isCharged (stack )) {
252+ cleanCrossbow (stack .getNbt (), fake , true );
253+ }
254+
217255 return fake ;
218256 }
219257
220258 /**
221- * Minimally copies over the banner NBT based on the provided blockEntity data.
259+ * Minimally copies over the crossbow data based on the provided Crossbow data.
260+ * <p>
261+ * Only the NBT required to render the crossbow, including rocket, is copied over.
262+ *
263+ * @param crossbow The raw crossbow NBT.
264+ * @param faked The faked ItemStack to copy to.
265+ * @param isInventory Whether to send the raw item or not.
266+ */
267+ static void cleanCrossbow (NbtCompound crossbow , ItemStack faked , boolean isInventory ) {
268+ if (crossbow == null ) return ;
269+ NbtList originalProjectiles = crossbow .getList ("ChargedProjectiles" , NbtElement .COMPOUND_TYPE );
270+ if (originalProjectiles .isEmpty ()) return ;
271+ String originalProjectile = originalProjectiles .getCompound (0 ).getString ("id" );
272+ String projectile ;
273+
274+ if (isInventory || "minecraft:firework" .equals (originalProjectile )) {
275+ projectile = originalProjectile ;
276+ } else {
277+ projectile = "minecraft:arrow" ;
278+ }
279+
280+ NbtCompound projectileStack = new NbtCompound ();
281+ projectileStack .putByte ("Count" , (byte ) 1 );
282+ projectileStack .putString ("id" , projectile );
283+ NbtList projectiles = new NbtList ();
284+ projectiles .add (projectileStack );
285+ faked .setSubNbt ("ChargedProjectiles" , projectiles );
286+ faked .setSubNbt ("Charged" , NbtByte .of (true ));
287+ }
288+
289+ /**
290+ * Minimally copies over the skull data based on the provided SkullOwner data.
291+ * <p>
292+ * Only the NBT required to render the skull is copied over.
222293 *
294+ * @param skullOwner The SkullOwner NBT compound. May be null.
295+ * @param faked The faked ItemStack to copy to.
296+ */
297+ static void cleanSkull (NbtCompound skullOwner , ItemStack faked ) {
298+ if (skullOwner != null ) {
299+ NbtCompound fakeSkullOwner = faked .getOrCreateSubNbt (SkullItem .SKULL_OWNER_KEY );
300+ if (skullOwner .containsUuid ("Id" )) fakeSkullOwner .putUuid ("Id" , skullOwner .getUuid ("Id" ));
301+ if (skullOwner .contains ("Properties" , NbtElement .COMPOUND_TYPE )) {
302+ NbtCompound skullProperties = skullOwner .getCompound ("Properties" );
303+ if (skullProperties .contains ("textures" , NbtElement .LIST_TYPE )) {
304+ NbtList skullTextures = skullProperties .getList ("textures" , NbtElement .COMPOUND_TYPE );
305+ if (!skullTextures .isEmpty ()) {
306+ NbtCompound skullValueContainer = skullTextures .getCompound (0 );
307+ String skullTexture = skullValueContainer .getString ("Value" );
308+ NbtCompound fakeProperties = new NbtCompound ();
309+ NbtList fakeTextures = new NbtList ();
310+ NbtCompound fakeTexture = new NbtCompound ();
311+ fakeTexture .putString ("Value" , skullTexture );
312+ fakeTextures .add (fakeTexture );
313+ fakeProperties .put ("textures" , fakeTextures );
314+ fakeSkullOwner .put ("Properties" , fakeProperties );
315+ }
316+ }
317+ }
318+ }
319+ }
320+
321+ /**
322+ * Minimally copies over the banner NBT based on the provided blockEntity data.
323+ * <p>
223324 * Only the NBT required to render a layer is copied over.
224325 *
225326 * @param blockEntity The original block entity tag. May be null.
226- * @param faked The faked ItemStack to copy to.
227- * * /
327+ * @param faked The faked ItemStack to copy to.
328+ */
228329 static void cleanBanner (NbtCompound blockEntity , ItemStack faked ) {
229330 if (blockEntity != null && blockEntity .contains ("Patterns" , NbtElement .LIST_TYPE )) {
230331 NbtList fakePatterns = new NbtList ();
@@ -247,7 +348,7 @@ static void cleanBanner(NbtCompound blockEntity, ItemStack faked) {
247348 }
248349 }
249350 }
250- faked .getOrCreateSubNbt ("BlockEntityTag" ).put ("Patterns" , fakePatterns );
351+ faked .getOrCreateSubNbt (BlockItem . BLOCK_STATE_TAG_KEY ).put ("Patterns" , fakePatterns );
251352 }
252353 }
253354}
0 commit comments