1+ package net .imprex .zip .nms .v1_21_R6 ;
2+
3+ import java .io .ByteArrayInputStream ;
4+ import java .io .IOException ;
5+ import java .lang .reflect .InvocationTargetException ;
6+ import java .lang .reflect .Method ;
7+ import java .util .ArrayList ;
8+ import java .util .List ;
9+ import java .util .UUID ;
10+ import java .util .function .BiConsumer ;
11+
12+ import org .bukkit .Material ;
13+ import org .bukkit .craftbukkit .v1_21_R6 .inventory .CraftItemStack ;
14+ import org .bukkit .inventory .ItemStack ;
15+ import org .bukkit .inventory .meta .SkullMeta ;
16+
17+ import com .google .gson .JsonArray ;
18+ import com .google .gson .JsonElement ;
19+ import com .google .gson .JsonObject ;
20+ import com .mojang .authlib .GameProfile ;
21+ import com .mojang .authlib .properties .Property ;
22+ import com .mojang .serialization .DataResult ;
23+ import com .mojang .serialization .Dynamic ;
24+ import com .mojang .serialization .DynamicOps ;
25+ import com .mojang .serialization .JsonOps ;
26+
27+ import net .imprex .zip .common .BPConstants ;
28+ import net .imprex .zip .common .ReflectionUtil ;
29+ import net .imprex .zip .nms .api .ItemStackContainerResult ;
30+ import net .imprex .zip .nms .api .ItemStackWithSlot ;
31+ import net .imprex .zip .nms .api .NmsManager ;
32+ import net .minecraft .SharedConstants ;
33+ import net .minecraft .core .RegistryAccess ;
34+ import net .minecraft .nbt .CompoundTag ;
35+ import net .minecraft .nbt .ListTag ;
36+ import net .minecraft .nbt .NbtAccounter ;
37+ import net .minecraft .nbt .NbtIo ;
38+ import net .minecraft .nbt .NbtOps ;
39+ import net .minecraft .nbt .Tag ;
40+ import net .minecraft .server .MinecraftServer ;
41+ import net .minecraft .util .datafix .DataFixers ;
42+ import net .minecraft .util .datafix .fixes .References ;
43+ import net .minecraft .world .item .component .ResolvableProfile ;
44+
45+ public class ZipNmsManager implements NmsManager {
46+
47+ private static final int DATA_VERSION = SharedConstants .getCurrentVersion ().dataVersion ().version ();
48+
49+ @ SuppressWarnings ("deprecation" )
50+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer .getServer ().registryAccess ();
51+
52+ private static final DynamicOps <Tag > DYNAMIC_OPS_NBT = DEFAULT_REGISTRY .createSerializationContext (NbtOps .INSTANCE );
53+ private static final DynamicOps <JsonElement > DYNAMIC_OPS_JSON = DEFAULT_REGISTRY .createSerializationContext (JsonOps .INSTANCE );
54+
55+ private static final BiConsumer <SkullMeta , GameProfile > SET_PROFILE ;
56+
57+ static {
58+ BiConsumer <SkullMeta , GameProfile > setProfile = (meta , profile ) -> {
59+ throw new NullPointerException ("Unable to find 'setProfile' method!" );
60+ };
61+
62+ Class <?> craftMetaSkullClass = new ItemStack (Material .PLAYER_HEAD )
63+ .getItemMeta ()
64+ .getClass ();
65+
66+ Method setResolvableProfileMethod = ReflectionUtil .searchMethod (craftMetaSkullClass , void .class , ResolvableProfile .class );
67+ if (setResolvableProfileMethod != null ) {
68+ setProfile = (meta , profile ) -> {
69+ try {
70+ setResolvableProfileMethod .invoke (meta , new ResolvableProfile (profile ));
71+ } catch (IllegalAccessException | InvocationTargetException e ) {
72+ e .printStackTrace ();
73+ }
74+ };
75+ } else {
76+ Method setProfileMethod = ReflectionUtil .searchMethod (craftMetaSkullClass , void .class , GameProfile .class );
77+ if (setProfileMethod != null ) {
78+ setProfile = (meta , profile ) -> {
79+ try {
80+ setProfileMethod .invoke (meta , profile );
81+ } catch (IllegalAccessException | InvocationTargetException e ) {
82+ e .printStackTrace ();
83+ }
84+ };
85+ }
86+ }
87+
88+ SET_PROFILE = setProfile ;
89+ }
90+
91+ @ Override
92+ public JsonObject itemstackToJsonElement (ItemStack [] items ) {
93+ JsonArray jsonItems = new JsonArray ();
94+ for (int slot = 0 ; slot < items .length ; slot ++) {
95+ ItemStack item = items [slot ];
96+ if (item == null || item .getType () == Material .AIR ) {
97+ continue ;
98+ }
99+ net .minecraft .world .item .ItemStack minecraftItem = CraftItemStack .asNMSCopy (item );
100+
101+ DataResult <JsonElement > result = net .minecraft .world .item .ItemStack .CODEC .encodeStart (DYNAMIC_OPS_JSON , minecraftItem );
102+ JsonObject resultJson = result .getOrThrow ().getAsJsonObject ();
103+
104+ resultJson .addProperty (BPConstants .KEY_INVENTORY_SLOT , slot );
105+ jsonItems .add (resultJson );
106+ }
107+
108+ JsonObject outputJson = new JsonObject ();
109+ outputJson .addProperty (BPConstants .KEY_INVENTORY_VERSION , BPConstants .INVENTORY_VERSION );
110+ outputJson .addProperty (BPConstants .KEY_INVENTORY_DATA_VERSION , DATA_VERSION );
111+ outputJson .addProperty (BPConstants .KEY_INVENTORY_ITEMS_SIZE , items .length );
112+ outputJson .add (BPConstants .KEY_INVENTORY_ITEMS , jsonItems );
113+ return outputJson ;
114+ }
115+
116+ @ Override
117+ public ItemStackContainerResult jsonElementToItemStack (JsonObject json ) {
118+ // check if current version the same
119+ if (json .get (BPConstants .KEY_INVENTORY_VERSION ).getAsInt () != BPConstants .INVENTORY_VERSION ) {
120+ throw new IllegalStateException ("Unable to convert binary to itemstack because zip version is missmatching" );
121+ }
122+
123+ int dataVersion = json .get (BPConstants .KEY_INVENTORY_DATA_VERSION ).getAsInt ();
124+ int itemsSize = json .get (BPConstants .KEY_INVENTORY_ITEMS_SIZE ).getAsInt ();
125+
126+ List <ItemStackWithSlot > items = new ArrayList <>();
127+
128+ JsonArray jsonItems = json .get (BPConstants .KEY_INVENTORY_ITEMS ).getAsJsonArray ();
129+ for (JsonElement item : jsonItems ) {
130+ Dynamic <JsonElement > dynamicItem = new Dynamic <>(JsonOps .INSTANCE , item );
131+ Dynamic <JsonElement > dynamicItemFixed = DataFixers .getDataFixer ()
132+ .update (References .ITEM_STACK , dynamicItem , dataVersion , DATA_VERSION );
133+
134+ net .minecraft .world .item .ItemStack minecraftItem = net .minecraft .world .item .ItemStack .CODEC
135+ .parse (DYNAMIC_OPS_JSON , dynamicItemFixed .getValue ())
136+ .getOrThrow ();
137+
138+ ItemStack bukkitItem = CraftItemStack .asCraftMirror (minecraftItem );
139+ int slot = item .getAsJsonObject ().get (BPConstants .KEY_INVENTORY_SLOT ).getAsInt ();
140+
141+ items .add (new ItemStackWithSlot (slot , bukkitItem ));
142+ }
143+
144+ return new ItemStackContainerResult (itemsSize , items );
145+ }
146+
147+ @ Override
148+ public JsonObject migrateToJsonElement (byte [] binary ) {
149+ CompoundTag compound ;
150+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream (binary )) {
151+ compound = NbtIo .readCompressed (inputStream , NbtAccounter .unlimitedHeap ());
152+ } catch (IOException e ) {
153+ throw new IllegalStateException ("Unable to parse binary to nbt" , e );
154+ }
155+
156+ ListTag list = compound .getListOrEmpty ("i" );
157+
158+ int currentSlot = 0 ;
159+
160+ JsonArray jsonItems = new JsonArray ();
161+ for (Tag base : list ) {
162+ if (base instanceof CompoundTag itemTag ) {
163+ String itemType = itemTag .getString ("id" ).orElse ("" );
164+ if (itemType .equals ("minecraft:air" )) {
165+ currentSlot ++;
166+ continue ;
167+ }
168+
169+ Dynamic <Tag > dynamicItem = new Dynamic <>(NbtOps .INSTANCE , itemTag );
170+ net .minecraft .world .item .ItemStack minecraftItem = net .minecraft .world .item .ItemStack .CODEC
171+ .parse (DYNAMIC_OPS_NBT , dynamicItem .getValue ())
172+ .getOrThrow ();
173+
174+ DataResult <JsonElement > result = net .minecraft .world .item .ItemStack .CODEC .encodeStart (DYNAMIC_OPS_JSON , minecraftItem );
175+ JsonObject resultJson = result .getOrThrow ().getAsJsonObject ();
176+
177+ resultJson .addProperty (BPConstants .KEY_INVENTORY_SLOT , currentSlot );
178+ jsonItems .add (resultJson );
179+
180+ currentSlot ++;
181+ }
182+ }
183+
184+ JsonObject json = new JsonObject ();
185+ json .addProperty (BPConstants .KEY_INVENTORY_VERSION , BPConstants .INVENTORY_VERSION );
186+ json .addProperty (BPConstants .KEY_INVENTORY_DATA_VERSION , DATA_VERSION );
187+ json .addProperty (BPConstants .KEY_INVENTORY_ITEMS_SIZE , list .size ());
188+ json .add (BPConstants .KEY_INVENTORY_ITEMS , jsonItems );
189+ return json ;
190+ }
191+
192+ @ Override
193+ public void setSkullProfile (SkullMeta meta , String texture ) {
194+ try {
195+ GameProfile gameProfile = new GameProfile (UUID .randomUUID (), "" );
196+ gameProfile .getProperties ().put ("textures" , new Property ("textures" , texture ));
197+
198+ SET_PROFILE .accept (meta , gameProfile );
199+ } catch (Exception e ) {
200+ e .printStackTrace ();
201+ }
202+ }
203+
204+ @ Override
205+ public boolean isAir (Material material ) {
206+ return material == null || material == Material .AIR ;
207+ }
208+ }
0 commit comments