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