Skip to content

Commit e306bdb

Browse files
committed
Add support for Minecraft 26.1
1 parent 43ec906 commit e306bdb

2 files changed

Lines changed: 365 additions & 1 deletion

File tree

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
package io.github.mrcomputer1.smileyplayertrader.versions;
2+
3+
import io.github.mrcomputer1.smileyplayertrader.util.merchant.MerchantRecipe;
4+
import org.bukkit.World;
5+
import org.bukkit.inventory.ItemStack;
6+
import org.bukkit.inventory.Merchant;
7+
import org.bukkit.inventory.MerchantInventory;
8+
import org.bukkit.inventory.meta.ItemMeta;
9+
10+
import java.io.ByteArrayInputStream;
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.InputStream;
13+
import java.io.OutputStream;
14+
import java.lang.reflect.Field;
15+
import java.lang.reflect.InvocationTargetException;
16+
import java.lang.reflect.Method;
17+
import java.util.List;
18+
19+
public class MCVersion26_1 implements IMCVersion {
20+
// Sources: NMS = net.minecraft.server, OBC = org.bukkit.craftbukkit
21+
// Source_ClassName_Static/Instance_MethodName_ArgTypes...
22+
23+
private Object registryAccess;
24+
private Object nbtOps;
25+
private Object itemStackCodec;
26+
27+
// NMS: net.minecraft.nbt.NbtAccounter
28+
private Method NMS_NbtAccounter_Static_unlimitedHeap_;
29+
30+
// NMS: net.minecraft.nbt.NbtIo
31+
private Method NMS_NbtIo_Static_readCompressed_InputStream_NbtAccounter;
32+
private Method NMS_NbtIo_Static_writeCompressed_CompoundTag_OutputStream;
33+
34+
// NMS: net.minecraft.world.item.ItemStack
35+
private Method NMS_ItemStack_Instance_isEmpty_;
36+
37+
// NMS: com.mojang.serialization.Codec
38+
private Method NMS_Codec_Instance_encodeStart_DynamicOps_Object;
39+
private Method NMS_Codec_Instance_parse_DynamicOps_Tag;
40+
41+
// NMS: net.minecraft.core.HolderLookup$Provider
42+
private Method NMS_HolderLookup$Provider_Instance_createSerializationContext_DynamicOps;
43+
44+
// NMS: com.mojang.serialisation.DataResult
45+
private Method NMS_DataResult_Instance_getOrThrow_;
46+
47+
// OBC: org.bukkit.craftbukkit.inventory.CraftItemStack
48+
private Method OBC_CraftItemStack_Static_asCraftMirror_ItemStack;
49+
private Method OBC_CraftItemStack_Static_asNMSCopy_ItemStack;
50+
51+
// OB: org.bukkit.inventory.MerchantRecipe
52+
private Field OB_MerchantRecipe_Instance_recipe;
53+
54+
// OBC: org.bukkit.craftbukkit._.inventory.CraftMerchant
55+
private Method OBC_CraftMerchant_Instance_getMerchant_;
56+
57+
// NMS: net.minecraft.world.item.trading.Merchant
58+
private Method NMS_Merchant_Instance_getOffers_;
59+
60+
// NMS: net.minecraft.world.item.trading.MerchantOffer
61+
private Method NMS_MerchantOffer_Instance_setSpecialPriceDiff_int;
62+
private Method NMS_MerchantOffer_Instance_getSpecialPriceDiff_;
63+
64+
// NMS: net.minecraft.world.item.trading.MerchantOffers
65+
private Method NMS_MerchantOffers_Instance_clear_;
66+
private Method NMS_MerchantOffers_Instance_add_MerchantOffer;
67+
68+
// OBC: org.bukkit.craftbukkit.inventory.CraftMerchantRecipe
69+
private Method OBC_CraftMerchantRecipe_Static_fromBukkit_MerchantRecipe;
70+
private Method OBC_CraftMerchantRecipe_Instance_toMinecraft_;
71+
72+
// OBC: org.bukkit.craftbukkit.inventory.CraftInventoryMerchant
73+
private Method OBC_CraftInventoryMerchant_Instance_getInventory_;
74+
75+
// NMS: net.minecraft.world.inventory.MerchantContainer
76+
private Method NMS_MerchantContainer_Instance_getActiveOffer_;
77+
78+
// OB: org.bukkit.inventory.meta.ItemMeta
79+
private Method OB_ItemMeta_Instance_hasItemName_;
80+
private Method OB_ItemMeta_Instance_getItemName_;
81+
82+
public MCVersion26_1(World world){
83+
try {
84+
/*
85+
* Classes
86+
*/
87+
Class<?> NMS_DynamicOps = Class.forName("com.mojang.serialization.DynamicOps");
88+
Class<?> NMS_NbtOps = Class.forName("net.minecraft.nbt.NbtOps");
89+
Class<?> NMS_HolderLookup$Provider = Class.forName("net.minecraft.core.HolderLookup$Provider");
90+
Class<?> NMS_Level = Class.forName("net.minecraft.world.level.Level");
91+
Class<?> NMS_CompoundTag = Class.forName("net.minecraft.nbt.CompoundTag");
92+
Class<?> NMS_NbtAccounter = Class.forName("net.minecraft.nbt.NbtAccounter");
93+
Class<?> NMS_NbtIo = Class.forName("net.minecraft.nbt.NbtIo");
94+
Class<?> NMS_ItemStack = Class.forName("net.minecraft.world.item.ItemStack");
95+
Class<?> NMS_Codec = Class.forName("com.mojang.serialization.Codec");
96+
Class<?> NMS_DataResult = Class.forName("com.mojang.serialization.DataResult");
97+
Class<?> NMS_Merchant = Class.forName("net.minecraft.world.item.trading.Merchant");
98+
Class<?> NMS_MerchantOffer = Class.forName("net.minecraft.world.item.trading.MerchantOffer");
99+
Class<?> NMS_MerchantOffers = Class.forName("net.minecraft.world.item.trading.MerchantOffers");
100+
Class<?> NMS_MerchantContainer = Class.forName("net.minecraft.world.inventory.MerchantContainer");
101+
102+
Class<?> OB_MerchantRecipe = Class.forName("org.bukkit.inventory.MerchantRecipe");
103+
Class<?> OB_ItemMeta = Class.forName("org.bukkit.inventory.meta.ItemMeta");
104+
Class<?> OBC_CraftWorld = Class.forName("org.bukkit.craftbukkit.CraftWorld");
105+
Class<?> OBC_CraftItemStack = Class.forName("org.bukkit.craftbukkit.inventory.CraftItemStack");
106+
Class<?> OBC_CraftMerchant = Class.forName("org.bukkit.craftbukkit.inventory.CraftMerchant");
107+
Class<?> OBC_CraftMerchantRecipe = Class.forName("org.bukkit.craftbukkit.inventory.CraftMerchantRecipe");
108+
Class<?> OBC_CraftInventoryMerchant = Class.forName("org.bukkit.craftbukkit.inventory.CraftInventoryMerchant");
109+
110+
/*
111+
* Fields and Methods
112+
*/
113+
// net.minecraft.nbt.NbtOps
114+
// Static Field: NMS: NbtOps.INSTANCE
115+
Field NMS_NbtOps_Static_INSTANCE = NMS_NbtOps.getField("INSTANCE");
116+
117+
// net.minecraft.core.HolderLookup$Provider
118+
// Instance Method: NMS: HolderLookup$Provider.createSerializationContext(DynamicOps)
119+
this.NMS_HolderLookup$Provider_Instance_createSerializationContext_DynamicOps =
120+
NMS_HolderLookup$Provider.getMethod("createSerializationContext", NMS_DynamicOps);
121+
122+
// org.bukkit.craftbukkit._.CraftWorld
123+
// Instance Method: OBC: CraftWorld.getHandle()
124+
Method OBC_CraftWorld_Instance_getHandle_ = OBC_CraftWorld.getMethod("getHandle");
125+
126+
// net.minecraft.world.level.Level
127+
// Instance Method: NMS: Level.registryAccess()
128+
Method NMS_Level_Instance_registryAccess_ = NMS_Level.getMethod("registryAccess");
129+
130+
// net.minecraft.nbt.NbtAccounter
131+
// Static Method: NMS: NbtAccounter.unlimitedHeap()
132+
this.NMS_NbtAccounter_Static_unlimitedHeap_ = NMS_NbtAccounter.getMethod("unlimitedHeap");
133+
134+
// net.minecraft.nbt.NbtIo
135+
// Static Method: NMS: NbtIo.readCompressed(InputStream, NbtAccounter)
136+
this.NMS_NbtIo_Static_readCompressed_InputStream_NbtAccounter =
137+
NMS_NbtIo.getMethod("readCompressed", InputStream.class, NMS_NbtAccounter);
138+
// Static Method: NMS: NbtIo.writeCompressed(CompoundTag, OutputStream)
139+
this.NMS_NbtIo_Static_writeCompressed_CompoundTag_OutputStream =
140+
NMS_NbtIo.getMethod("writeCompressed", NMS_CompoundTag, OutputStream.class);
141+
142+
// net.minecraft.world.item.ItemStack
143+
// Static Field: NMS: ItemStack.CODEC
144+
Field NMS_ItemStack_Static_CODEC = NMS_ItemStack.getField("CODEC");
145+
// Instance Method: NMS: ItemStack.isEmpty()
146+
this.NMS_ItemStack_Instance_isEmpty_ = NMS_ItemStack.getMethod("isEmpty");
147+
148+
// com.mojang.serialization.Codec
149+
// Instance Method: NMS: Codec.encodeStart(DynamicOps, Object)
150+
this.NMS_Codec_Instance_encodeStart_DynamicOps_Object =
151+
NMS_Codec.getMethod("encodeStart", NMS_DynamicOps, Object.class);
152+
// Instance Method: NMS: Codec.parse(DynamicOps, Tag/Object)
153+
this.NMS_Codec_Instance_parse_DynamicOps_Tag =
154+
NMS_Codec.getMethod("parse", NMS_DynamicOps, Object.class);
155+
156+
// com.mojang.serialization.DataResult
157+
// Instance Method: NMS: DataResult.getOrThrow()
158+
this.NMS_DataResult_Instance_getOrThrow_ = NMS_DataResult.getMethod("getOrThrow");
159+
160+
// org.bukkit.craftbukkit._.inventory.CraftItemStack
161+
// Static Method: OBC: CraftItemStack.asCraftMirror(NMS: ItemStack)
162+
this.OBC_CraftItemStack_Static_asCraftMirror_ItemStack =
163+
OBC_CraftItemStack.getMethod("asCraftMirror", NMS_ItemStack);
164+
// Static Method: OBC: CraftItemStack.asNMSCopy(ItemStack)
165+
this.OBC_CraftItemStack_Static_asNMSCopy_ItemStack =
166+
OBC_CraftItemStack.getMethod("asNMSCopy", ItemStack.class);
167+
168+
// org.bukkit.inventory.MerchantRecipe
169+
// Instance Field: OB: MerchantRecipe.result
170+
this.OB_MerchantRecipe_Instance_recipe = OB_MerchantRecipe.getDeclaredField("result");
171+
this.OB_MerchantRecipe_Instance_recipe.setAccessible(true);
172+
173+
// org.bukkit.craftbukkit._.inventory.CraftMerchant
174+
// Instance Method: OBC: CraftMerchant.getMerchant()
175+
this.OBC_CraftMerchant_Instance_getMerchant_ = OBC_CraftMerchant.getMethod("getMerchant");
176+
177+
// net.minecraft.world.item.trading.Merchant
178+
// Instance Method: NMS: Merchant.getOffers()
179+
this.NMS_Merchant_Instance_getOffers_ = NMS_Merchant.getMethod("getOffers");
180+
181+
// net.minecraft.world.item.trading.MerchantOffer
182+
// Instance Method: NMS: MerchantOffer.setSpecialPriceDiff(int)
183+
this.NMS_MerchantOffer_Instance_setSpecialPriceDiff_int = NMS_MerchantOffer.getMethod("setSpecialPriceDiff", int.class);
184+
// Instance Method: NMS: MerchantOffer.getSpecialPriceDiff()
185+
this.NMS_MerchantOffer_Instance_getSpecialPriceDiff_ = NMS_MerchantOffer.getMethod("getSpecialPriceDiff");
186+
187+
// net.minecraft.world.item.trading.MerchantOffers
188+
// Instance Method: NMS: MerchantOffers.clear()
189+
this.NMS_MerchantOffers_Instance_clear_ = NMS_MerchantOffers.getMethod("clear");
190+
// Instance Method: NMS: MerchantOffers.add(MerchantOffer)
191+
this.NMS_MerchantOffers_Instance_add_MerchantOffer =
192+
NMS_MerchantOffers.getMethod("add", Object.class);
193+
194+
// org.bukkit.craftbukkit._.inventory.CraftMerchantRecipe
195+
// Static Method: OBC: CraftMerchantRecipe.fromBukkit(MerchantRecipe)
196+
this.OBC_CraftMerchantRecipe_Static_fromBukkit_MerchantRecipe =
197+
OBC_CraftMerchantRecipe.getMethod("fromBukkit", org.bukkit.inventory.MerchantRecipe.class);
198+
// Instance Method: OBC: CraftMerchantRecipe.toMinecraft()
199+
this.OBC_CraftMerchantRecipe_Instance_toMinecraft_ = OBC_CraftMerchantRecipe.getMethod("toMinecraft");
200+
201+
// org.bukkit.craftbukkit._.inventory.CraftInventoryMerchant
202+
// Instance Method: OBC: CraftInventoryMerchant.getInventory()
203+
this.OBC_CraftInventoryMerchant_Instance_getInventory_ =
204+
OBC_CraftInventoryMerchant.getMethod("getInventory");
205+
206+
// net.minecraft.world.inventory.MerchantContainer
207+
// Instance Method: NMS: MerchantContainer.getActiveOffer()
208+
this.NMS_MerchantContainer_Instance_getActiveOffer_ = NMS_MerchantContainer.getMethod("getActiveOffer");
209+
210+
// org.bukkit.inventory.meta.ItemMeta
211+
// Instance Method: OB: ItemMeta.hasItemName()
212+
//noinspection JavaReflectionMemberAccess
213+
this.OB_ItemMeta_Instance_hasItemName_ = OB_ItemMeta.getMethod("hasItemName");
214+
//noinspection JavaReflectionMemberAccess
215+
this.OB_ItemMeta_Instance_getItemName_ = OB_ItemMeta.getMethod("getItemName");
216+
217+
/*
218+
* Constants
219+
*/
220+
// this.nbtOps = NbtOps.INSTANCE
221+
this.nbtOps = NMS_NbtOps_Static_INSTANCE.get(null);
222+
223+
// Object nmsWorld = CraftWorld.getHandle(world)
224+
Object nmsWorld = OBC_CraftWorld_Instance_getHandle_.invoke(world);
225+
226+
// this.registryAccess = nmsWorld.registryAccess()
227+
this.registryAccess = NMS_Level_Instance_registryAccess_.invoke(nmsWorld);
228+
229+
// this.itemStackCodec = ItemStack.CODEC
230+
this.itemStackCodec = NMS_ItemStack_Static_CODEC.get(null);
231+
} catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException e) {
232+
e.printStackTrace();
233+
}
234+
}
235+
236+
@Override
237+
public ItemStack byteArrayToItemStack(byte[] array) throws InvocationTargetException {
238+
try {
239+
// NbtAccounter readLimiter = NbtAccounter.unlimitedHeap()
240+
Object readLimiter = NMS_NbtAccounter_Static_unlimitedHeap_.invoke(null);
241+
// CompoundTag tagCompound = NbtIo.readCompressed(new ByteArrayInputStream(array), readLimiter)
242+
Object tagCompound = NMS_NbtIo_Static_readCompressed_InputStream_NbtAccounter.invoke(null, new ByteArrayInputStream(array), readLimiter);
243+
244+
// DynamicOps dynamicOps = registryAccess.createSerializationContext(nbtOps)
245+
Object dynamicOps =
246+
NMS_HolderLookup$Provider_Instance_createSerializationContext_DynamicOps.invoke(registryAccess, nbtOps);
247+
248+
// DataResult dataResult = itemStackCodec.parse(dynamicOps, tagCompound)
249+
Object dataResult =
250+
NMS_Codec_Instance_parse_DynamicOps_Tag.invoke(itemStackCodec, dynamicOps, tagCompound);
251+
252+
// NMS:ItemStack nmsis = dataResult.getOrThrow()
253+
Object nmsis = NMS_DataResult_Instance_getOrThrow_.invoke(dataResult);
254+
// CraftItemStack cis = CraftItemStack.asCraftMirror(nmsis)
255+
Object cis = OBC_CraftItemStack_Static_asCraftMirror_ItemStack.invoke(null, nmsis);
256+
return (ItemStack)cis;
257+
} catch (IllegalAccessException e) {
258+
e.printStackTrace();
259+
}
260+
return null;
261+
}
262+
263+
@Override
264+
public byte[] itemStackToByteArray(ItemStack itemStack) throws InvocationTargetException{
265+
try {
266+
// NMS:ItemStack nmsis = CraftItemStack.asNMSCopy(itemStack)
267+
Object nmsis = OBC_CraftItemStack_Static_asNMSCopy_ItemStack.invoke(null, itemStack);
268+
269+
// if (nmsis.isEmpty())
270+
if ((boolean) NMS_ItemStack_Instance_isEmpty_.invoke(nmsis))
271+
throw new IllegalStateException("Empty items cannot be encoded.");
272+
273+
// DynamicOps dynamicOps = registryAccess.createSerializationContext(nbtOps)
274+
Object dynamicOps =
275+
NMS_HolderLookup$Provider_Instance_createSerializationContext_DynamicOps.invoke(registryAccess, nbtOps);
276+
277+
// DataResult dataResult = itemStackCodec.encodeStart(dynamicOps, nmsis)
278+
Object dataResult =
279+
NMS_Codec_Instance_encodeStart_DynamicOps_Object.invoke(itemStackCodec, dynamicOps, nmsis);
280+
281+
// CompoundTag tagCompound = dataResult.getOrThrow()
282+
Object tagCompound = NMS_DataResult_Instance_getOrThrow_.invoke(dataResult);
283+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
284+
// NbtIo.writeCompressed(tagCompound, baos)
285+
NMS_NbtIo_Static_writeCompressed_CompoundTag_OutputStream.invoke(null, tagCompound, baos);
286+
return baos.toByteArray();
287+
} catch (IllegalAccessException e) {
288+
e.printStackTrace();
289+
}
290+
return null;
291+
}
292+
293+
@Override
294+
public void setRecipesOnMerchant(Merchant merchant, List<MerchantRecipe> recipes) throws InvocationTargetException {
295+
try {
296+
// Merchant im = CraftMerchant.getMerchant(merchant)
297+
Object im = OBC_CraftMerchant_Instance_getMerchant_.invoke(merchant);
298+
// MerchantOffers offers = im.getOffers()
299+
Object offers = NMS_Merchant_Instance_getOffers_.invoke(im);
300+
// offers.clear()
301+
NMS_MerchantOffers_Instance_clear_.invoke(offers);
302+
303+
for(MerchantRecipe recipe : recipes){
304+
// CraftMerchantRecipe cmr = CraftMerchantRecipe.fromBukkit(recipe)
305+
Object cmr = OBC_CraftMerchantRecipe_Static_fromBukkit_MerchantRecipe.invoke(null, recipe);
306+
// MerchantOffer mr = cmr.toMinecraft()
307+
Object mr = OBC_CraftMerchantRecipe_Instance_toMinecraft_.invoke(cmr);
308+
// mr.setSpecialPriceDiff(recipe.getSpecialPrice())
309+
NMS_MerchantOffer_Instance_setSpecialPriceDiff_int.invoke(mr, recipe.getSpecialPrice());
310+
// offers.add(mr)
311+
NMS_MerchantOffers_Instance_add_MerchantOffer.invoke(offers, mr);
312+
}
313+
} catch (IllegalAccessException | InvocationTargetException e) {
314+
e.printStackTrace();
315+
}
316+
}
317+
318+
@Override
319+
public int getSpecialCountForRecipe(MerchantInventory inventory) throws InvocationTargetException{
320+
try {
321+
// MerchantContainer im = inventory.getInventory()
322+
Object im = OBC_CraftInventoryMerchant_Instance_getInventory_.invoke(inventory);
323+
// MerchantOffer mr = im.getActiveOffer()
324+
Object mr = NMS_MerchantContainer_Instance_getActiveOffer_.invoke(im);
325+
// return mr.getSpecialPriceDiff()
326+
return (int) NMS_MerchantOffer_Instance_getSpecialPriceDiff_.invoke(mr);
327+
} catch (IllegalAccessException e) {
328+
e.printStackTrace();
329+
return 0;
330+
}
331+
}
332+
333+
@Override
334+
public ItemStack getMerchantRecipeOriginalResult(org.bukkit.inventory.MerchantRecipe merchantRecipe) {
335+
try {
336+
// return (ItemStack) merchantRecipe.recipe
337+
return (ItemStack) OB_MerchantRecipe_Instance_recipe.get(merchantRecipe);
338+
} catch (IllegalAccessException e) {
339+
throw new RuntimeException(e);
340+
}
341+
}
342+
343+
@Override
344+
public String getPreferredItemName(ItemMeta itemMeta) {
345+
try {
346+
if (itemMeta.hasDisplayName()) {
347+
return itemMeta.getDisplayName();
348+
} else if ((boolean) OB_ItemMeta_Instance_hasItemName_.invoke(itemMeta)) {
349+
return (String) OB_ItemMeta_Instance_getItemName_.invoke(itemMeta);
350+
} else {
351+
return null;
352+
}
353+
} catch (InvocationTargetException | IllegalAccessException e) {
354+
throw new RuntimeException(e);
355+
}
356+
}
357+
358+
}

src/main/java/io/github/mrcomputer1/smileyplayertrader/versions/VersionSupport.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,15 @@ public VersionSupportMeta(Callable<Boolean> isSupported, Supplier<? extends IMCV
162162

163163
// 1.21.11
164164
registerSupportedVersion(
165-
() -> Pattern.compile("^1\\.21\\.(1[1-9])-").matcher(bukkitVersion).find(),
165+
() -> Pattern.compile("^1\\.21\\.11-").matcher(bukkitVersion).find(),
166166
() -> new MCVersion1_21_R7(Bukkit.getWorlds().get(0))
167167
);
168+
169+
// 26.1
170+
registerSupportedVersion(
171+
() -> Pattern.compile("^26\\.[1-9](\\.[0-9]+)?-").matcher(bukkitVersion).find(),
172+
() -> new MCVersion26_1(Bukkit.getWorlds().get(0))
173+
);
168174
}
169175

170176
public static void registerSupportedVersion(Callable<Boolean> isSupported, Supplier<? extends IMCVersion> versionSupplier){

0 commit comments

Comments
 (0)