Skip to content

Commit 1d3a155

Browse files
committed
Improve the logic so you could create sub custom values
Make it easier to create custom nbt data instead of set it directly in the compound. So it get a nested sty like bukkit does.
1 parent 9a552bc commit 1d3a155

File tree

2 files changed

+226
-42
lines changed

2 files changed

+226
-42
lines changed

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/utility/compound/LegacyNBT.java

Lines changed: 225 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.lang.invoke.MethodHandles;
1212
import java.lang.invoke.MethodType;
1313
import java.lang.reflect.Constructor;
14+
import java.util.Arrays;
1415
import java.util.logging.Level;
1516

1617
/**
@@ -90,9 +91,15 @@ public static class NmsItemSession {
9091
private static final MethodHandle GET_TAG;
9192
private static final MethodHandle SET_TAG;
9293
private static final Constructor<?> nbtTagConstructor;
93-
private static final boolean REFLECTION_READY;
94+
private static final MethodHandle GET_COMPOUND;
95+
private static final MethodHandle GET_NESTED_COMPOUND;
96+
private static final MethodHandle SET_NESTED_COMPOUND;
9497

98+
private static final boolean REFLECTION_READY;
9599
private final Object nmsItemCopy;
100+
private final ItemStack bukkitItem;
101+
private boolean finalize = false;
102+
private CompoundState compoundState = CompoundState.NOT_CREATED;
96103

97104
static {
98105
Constructor<?> tagConstructor = null;
@@ -101,6 +108,9 @@ public static class NmsItemSession {
101108
MethodHandle hasNBTTag = null;
102109
MethodHandle bukkitCopy = null;
103110
MethodHandle nMSCopyItem = null;
111+
MethodHandle getCompoundM = null;
112+
MethodHandle getNestedCompound = null;
113+
MethodHandle setNestedCompound = null;
104114
boolean reflectionDone = false;
105115

106116
try {
@@ -110,6 +120,8 @@ public static class NmsItemSession {
110120
final Class<?> craftItemStack = Class.forName(craftPath + ".inventory.CraftItemStack");
111121
final Class<?> nmsItemStack = Class.forName(nmsPath + ".ItemStack");
112122
final Class<?> nbtTagCompound = Class.forName(getNbtTagPath());
123+
final Class<?> nbtTagBase = Class.forName(getNbtTagBasePath());
124+
113125
final MethodHandles.Lookup lookup = MethodHandles.lookup();
114126

115127
nMSCopyItem = lookup.findStatic(craftItemStack, "asNMSCopy",
@@ -124,24 +136,31 @@ public static class NmsItemSession {
124136
setNBTTag = lookup.findVirtual(nmsItemStack, "setTag",
125137
MethodType.methodType(void.class, nbtTagCompound));
126138

139+
setNestedCompound = lookup.findVirtual(nbtTagCompound, "set", MethodType.methodType(void.class, String.class, nbtTagBase));
140+
getNestedCompound = lookup.findVirtual(nbtTagCompound, "get", MethodType.methodType(nbtTagBase, String.class));
141+
getCompoundM = lookup.findVirtual(nbtTagCompound, "getCompound", MethodType.methodType(nbtTagCompound, String.class));
142+
127143
tagConstructor = nbtTagCompound.getConstructor();
128144
reflectionDone = true;
129-
} catch (ClassNotFoundException | NoSuchMethodException e) {
130-
logger.logError(e, () -> "Failed to initialize all NMS methods needed.");
131-
} catch (IllegalAccessException e) {
132-
throw new RuntimeException(e);
145+
} catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) {
146+
logger.logError(e, () -> "Failed to initialize all NMS methods needed for legacy minecraft.");
133147
}
134148
nbtTagConstructor = tagConstructor;
135149
SET_TAG = setNBTTag;
136150
GET_TAG = getNBTTag;
137151
HAS_TAG = hasNBTTag;
138152
AS_BUKKIT_ITEM_COPY = bukkitCopy;
139153
NMS_ITEM_COPY = nMSCopyItem;
154+
GET_COMPOUND = getCompoundM;
155+
GET_NESTED_COMPOUND = getNestedCompound;
156+
SET_NESTED_COMPOUND = setNestedCompound;
140157
REFLECTION_READY = reflectionDone;
158+
141159
}
142160

143161
private NmsItemSession(@Nonnull ItemStack item) {
144162
this.nmsItemCopy = toNmsItemStack(item);
163+
this.bukkitItem = item.clone();
145164
}
146165

147166
/**
@@ -172,64 +191,130 @@ public boolean hasTag() {
172191
}
173192

174193
/**
175-
* Returns the existing {@link CompoundTag} if one is present,
176-
* otherwise creates a new one.
194+
* Checks whether this item contains an {@code NBTTagCompound} with the given name.
195+
* <p>
196+
* If the name is empty, the <strong>root compound</strong> is evaluated instead.
197+
* Both root and nested compounds are considered valid targets.
198+
*
199+
* @param name the custom key of the nested compound. To target the root compound,
200+
* use an empty string or {@link #hasTag()}.
201+
* @return {@code true} if the specified (or root) compound exists
202+
*/
203+
public boolean hasTag(@Nonnull final String name) {
204+
try {
205+
if (!hasTag()) return false;
206+
Object root = GET_TAG.invoke(nmsItemCopy);
207+
if (name.isEmpty()) return root != null;
208+
Object nested = GET_NESTED_COMPOUND.invoke(root, name);
209+
return nested != null;
210+
} catch (Throwable ignored) {
211+
return false;
212+
}
213+
}
214+
215+
/**
216+
* Returns the root {@link CompoundTag} of this item, creating one if it does not exist.
217+
* <p>
218+
* This method always operates on the root compound. If you want a nested compound,
219+
* use {@link #getOrCreateCompound(String)} with a specific name.
177220
*
178-
* @return the existing {@link CompoundTag} or a new instance if none exists.
179-
* Returns {@code null} only if reflection failed or the underlying
180-
* NBTTagCompound could not be created.
221+
* @return the root {@link CompoundTag}, never {@code null} unless reflection failed.
181222
*/
182223
@Nullable
183224
public CompoundTag getOrCreateCompound() {
184-
try {
185-
Object tag;
186-
if (hasTag()) {
187-
tag = GET_TAG.invoke(nmsItemCopy);
188-
} else {
189-
tag = nbtTagConstructor.newInstance();
190-
SET_TAG.invoke(nmsItemCopy, tag);
191-
}
192-
return new CompoundTag(tag);
225+
return getCompoundTag("", false);
226+
}
193227

194-
} catch (Throwable e) {
195-
logger.logError(e, () -> "Failed to initialize CompoundTag");
196-
}
197-
return null;
228+
/**
229+
* Returns a {@link CompoundTag} with the given name, creating it if it does not exist.
230+
* <p>
231+
* If {@code name} is empty (""), this will return the root compound, which is equivalent
232+
* to {@link #getOrCreateCompound()}.
233+
* <p>
234+
* Use a non-empty name if you want a nested compound separate from the root.
235+
*
236+
* @param name the name of the nested compound, or empty string for root.
237+
* @return the existing or newly created {@link CompoundTag}, or {@code null} if reflection failed.
238+
*/
239+
@Nullable
240+
public CompoundTag getOrCreateCompound(@Nonnull final String name) {
241+
return getCompoundTag(name, true);
198242
}
199243

244+
200245
/**
201-
* Returns the existing {@link CompoundTag} if one is present.
246+
* Returns the root {@link CompoundTag} if present.
247+
* <p>
248+
* This method does not create a new compound. Use {@link #getOrCreateCompound()} to
249+
* create a root compound if it does not exist.
202250
*
203-
* @return the existing {@link CompoundTag} or {@code null} if none exists.
251+
* @return the root {@link CompoundTag} if present, otherwise {@code null}.
204252
*/
205253
@Nullable
206254
public CompoundTag getCompound() {
207-
try {
208-
if (!hasTag()) return null;
209-
return new CompoundTag(GET_TAG.invoke(this.nmsItemCopy));
210-
} catch (Throwable e) {
211-
logger.logError(e, () -> "Failed to initialize CompoundTag");
212-
}
213-
return null;
255+
return getCompound("", false);
214256
}
215257

216258
/**
217-
* Applies the current CompoundTag to the ItemStack and returns
218-
* a new Bukkit ItemStack instance.
259+
* Returns the {@link CompoundTag} with the given name if present.
260+
* <p>
261+
* If {@code name} is empty (""), this returns the root compound.
262+
* For a nested compound, pass a non-empty name.
263+
* <p>
264+
* This method does not create a compound; use {@link #getOrCreateCompound(String)}
265+
* to create one if it does not exist.
219266
*
220-
* @param tag the {@link CompoundTag} instance that wraps NBTTagCompound.
221-
* @return Returns the copy of your itemStack with the nbt set.
267+
* @param name the name of the nested compound, or empty string for root.
268+
* @return the existing {@link CompoundTag} if present, otherwise {@code null}.
222269
*/
223270
@Nullable
224-
public ItemStack apply(@Nonnull final CompoundTag tag) {
225-
if (!REFLECTION_READY || nmsItemCopy == null) return null;
271+
public CompoundTag getCompound(@Nonnull final String name) {
272+
return getCompound(name, true);
273+
}
274+
275+
/**
276+
* Applies the current NBT data of this item to the underlying {@link ItemStack} and
277+
* returns a new Bukkit {@link ItemStack} instance.
278+
* <p>
279+
* This method always applies the root {@link CompoundTag}, including any nested compounds
280+
* created via {@link #getOrCreateCompound(String)}. The returned item will contain the
281+
* full NBT structure currently set in this session.
282+
* <p>
283+
* The method checks the {@link CompoundState} before applying changes:
284+
* <ul>
285+
* <li>{@link CompoundState#CREATED}: Compound exists and will be applied.</li>
286+
* <li>{@link CompoundState#NULL}: No compound exists, nothing is applied.</li>
287+
* <li>{@link CompoundState#ERROR}: Reflection failed or compound initialization failed,
288+
* nothing is applied.</li>
289+
* <li>{@link CompoundState#NOT_CREATED}: No compound has been created yet.</li>
290+
* </ul>
291+
* <p>
292+
* Use {@link #getOrCreateCompound()} or {@link #getOrCreateCompound(String)} to ensure a
293+
* compound exists before calling this method.
294+
*
295+
* @return a new {@link ItemStack} containing the applied NBT, or the original {@link ItemStack}
296+
* if the compound was not created or an error occurred.
297+
*/
298+
@Nonnull
299+
public ItemStack finalizeChanges() {
300+
if (!REFLECTION_READY || nmsItemCopy == null) return this.bukkitItem;
301+
if (compoundState != CompoundState.CREATED) {
302+
logger.log(() -> "FinalizeChanges: " + compoundState.getMessage());
303+
return this.bukkitItem;
304+
}
226305
try {
227-
SET_TAG.invoke(nmsItemCopy, tag.getHandle());
306+
Object compound = GET_TAG.invoke(nmsItemCopy);
307+
if (compound == null) {
308+
logger.log(() -> "Failed to initialize the item creation, because the compound is not created yet.");
309+
return this.bukkitItem;
310+
}
311+
SET_TAG.invoke(nmsItemCopy, compound);
312+
finalize = true;
228313
return (ItemStack) AS_BUKKIT_ITEM_COPY.invoke(nmsItemCopy);
229314
} catch (Throwable e) {
230315
logger.logError(e, () -> "Failed to apply back to itemStack");
231316
}
232-
return null;
317+
return this.bukkitItem;
233318
}
234319

235320
/**
@@ -247,6 +332,79 @@ private Object toNmsItemStack(@Nonnull final ItemStack item) {
247332
return null;
248333
}
249334

335+
/**
336+
* Internal helper to get or create a {@link CompoundTag}.
337+
* <p>
338+
* If {@code usingName} is true and the {@code name} parameter is empty,
339+
* a debug log is written suggesting to use {@link #getOrCreateCompound()} instead
340+
* for the root compound.
341+
*
342+
* @param name the name of the nested compound; empty string returns the root.
343+
* @param usingName whether this call is intended for a named compound.
344+
* @return the requested {@link CompoundTag}, or null if reflection fails.
345+
*/
346+
private CompoundTag getCompoundTag(final String name, final boolean usingName) {
347+
if (usingName && name.isEmpty())
348+
logger.log(Level.FINE, () -> "Empty string passed to getOrCreateCompound(name). Use getOrCreateCompound() for root instead.");
349+
try {
350+
Object root;
351+
Object nested = null;
352+
if (hasTag()) {
353+
root = GET_TAG.invoke(nmsItemCopy);
354+
} else {
355+
root = nbtTagConstructor.newInstance();
356+
SET_TAG.invoke(nmsItemCopy, root);
357+
}
358+
if (!name.isEmpty()) {
359+
nested = GET_NESTED_COMPOUND.invoke(root, name);
360+
if (nested == null) {
361+
nested = nbtTagConstructor.newInstance();
362+
SET_NESTED_COMPOUND.invoke(root, name, nested);
363+
}
364+
}
365+
this.compoundState = CompoundState.CREATED;
366+
return new CompoundTag(nested != null ? nested : root);
367+
} catch (Throwable e) {
368+
logger.logError(e, () -> "Failed to initialize CompoundTag");
369+
this.compoundState = CompoundState.ERROR;
370+
}
371+
return null;
372+
}
373+
374+
/**
375+
* Internal helper to get a {@link CompoundTag} if present.
376+
* <p>
377+
* If {@code usingName} is true and the {@code name} parameter is empty,
378+
* a debug log is written suggesting to use {@link #getCompound()} instead
379+
* for the root compound.
380+
*
381+
* @param name the name of the nested compound; empty string returns the root.
382+
* @param usingName whether this call is intended for a named compound.
383+
* @return the requested {@link CompoundTag}, or null if not present or reflection fails.
384+
*/
385+
private CompoundTag getCompound(final String name, final boolean usingName) {
386+
if (usingName && name.isEmpty())
387+
logger.log(Level.FINE, () -> "Empty string passed to getCompound(name). Use getCompound() for root instead.");
388+
389+
try {
390+
if (!hasTag()) return null;
391+
Object root = GET_TAG.invoke(nmsItemCopy);
392+
Object nested = null;
393+
if (!name.isEmpty()) {
394+
nested = GET_NESTED_COMPOUND.invoke(root, name);
395+
if (nested == null) {
396+
logger.log(Level.CONFIG, () -> "Requested nested compound '" + name + "' not found. Returning root instead.");
397+
}
398+
}
399+
this.compoundState = CompoundState.CREATED;
400+
return new CompoundTag(nested != null ? nested : root);
401+
} catch (Throwable e) {
402+
logger.logError(e, () -> "Failed to initialize CompoundTag");
403+
this.compoundState = CompoundState.ERROR;
404+
}
405+
return null;
406+
}
407+
250408
private static String getCraftBukkitPath() {
251409
return "org.bukkit.craftbukkit." + getPackageVersion();
252410
}
@@ -279,6 +437,14 @@ public static class CompoundSession {
279437
final Class<?> nbtTag = Class.forName(getNbtTagPath());
280438
final MethodHandles.Lookup lookup = MethodHandles.lookup();
281439

440+
Arrays.stream(nbtTag.getMethods()).forEach(method -> {
441+
System.out.println("######################################");
442+
System.out.println("m " + method.getName());
443+
System.out.println("ParameterTypes " + Arrays.toString(method.getParameterTypes()));
444+
System.out.println("ReturnType " + method.getReturnType());
445+
});
446+
447+
282448
hasTagKey = lookup.findVirtual(nbtTag, "hasKey",
283449
MethodType.methodType(boolean.class, String.class));
284450
removeM = lookup.findVirtual(nbtTag, "remove",
@@ -400,6 +566,10 @@ private static String getNbtTagPath() {
400566
return getNmsPath() + ".NBTTagCompound";
401567
}
402568

569+
private static String getNbtTagBasePath() {
570+
return getNmsPath() + ".NBTBase";
571+
}
572+
403573
private static String getNmsPath() {
404574
return "net.minecraft.server." + getPackageVersion();
405575
}
@@ -416,4 +586,19 @@ private static String getPackageVersion() {
416586
return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
417587
}
418588

589+
public enum CompoundState {
590+
ERROR("Failed to initialize compound"),
591+
CREATED("Compound created successfully"),
592+
NULL("Compound is null"),
593+
NOT_CREATED("Compound is not set yet, can be null");
594+
private final String message;
595+
596+
CompoundState(String message) {
597+
this.message = message;
598+
}
599+
600+
public String getMessage() {
601+
return message;
602+
}
603+
}
419604
}

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/utility/compound/NbtData.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.broken.arrow.library.itemcreator.utility.compound;
22

33

4-
import org.broken.arrow.library.logging.Logging;
54
import org.broken.arrow.library.logging.Validate;
65
import org.bukkit.inventory.ItemStack;
76

@@ -82,7 +81,7 @@ public CompoundTag getCompound() {
8281
*/
8382
@Nullable
8483
public ItemStack apply(@Nonnull final CompoundTag tag) {
85-
return this.session.apply(tag);
84+
return this.session.finalizeChanges();
8685
}
8786

8887
}

0 commit comments

Comments
 (0)