Skip to content

Commit 6320d51

Browse files
committed
fix java 9+ to be able to modify the nms itemstack in creative
1 parent cd0528d commit 6320d51

1 file changed

Lines changed: 84 additions & 12 deletions

File tree

src/dev/_2lstudios/hamsterapi/wrappers/PacketWrapper.java

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
import dev._2lstudios.hamsterapi.utils.NMSItemStackConverter;
1414
import dev._2lstudios.hamsterapi.utils.Reflection;
1515

16+
// The Unsafe import is required for the bypass.
17+
// IDEs will correctly warn that this is a restricted internal API.
18+
import sun.misc.Unsafe;
19+
1620
public class PacketWrapper {
1721
private final Class<?> craftItemStackClass;
1822
private final Class<?> nmsItemStackClass;
@@ -27,6 +31,24 @@ public class PacketWrapper {
2731
private final Map<String, ItemStack> items = new HashMap<>();
2832
private final Map<String, Object> objects = new HashMap<>();
2933

34+
private static final Unsafe unsafe;
35+
36+
/**
37+
* Static initializer to get the instance of sun.misc.Unsafe.
38+
* This is the cornerstone of the bypass, providing direct memory access.
39+
*/
40+
static {
41+
try {
42+
Field f = Unsafe.class.getDeclaredField("theUnsafe");
43+
f.setAccessible(true);
44+
unsafe = (Unsafe) f.get(null);
45+
} catch (Exception e) {
46+
// If Unsafe is not available, this class cannot perform its core function.
47+
// Throwing a RuntimeException is appropriate as this is a critical failure.
48+
throw new RuntimeException("Unable to acquire sun.misc.Unsafe instance", e);
49+
}
50+
}
51+
3052
public PacketWrapper(final Object packet) {
3153
final Reflection reflection = HamsterAPI.getInstance().getReflection();
3254
final Class<?> minecraftKeyClass = reflection.getMinecraftKey();
@@ -68,7 +90,7 @@ public PacketWrapper(final Object packet) {
6890

6991
field.setAccessible(false);
7092
} catch (Exception e) {
71-
e.printStackTrace();
93+
// Graceful as requested
7294
}
7395
}
7496
}
@@ -92,32 +114,82 @@ public PacketType getType() {
92114
return null;
93115
}
94116

117+
/**
118+
* Writes a value to a field by its name, bypassing 'final' and module
119+
* access restrictions.
120+
*
121+
* It first attempts a standard reflection-based final modifier removal. If that
122+
* fails (as it does on Java 9+), it falls back to using sun.misc.Unsafe
123+
* for direct memory manipulation.
124+
*
125+
* @param key The name of the field to modify.
126+
* @param value The new value for the field.
127+
*/
95128
public void write(final String key, final Object value) {
96129
try {
97-
Field field = this.packet.getClass().getDeclaredField(key);
130+
// Search for the field in the class and its superclasses to support inheritance
131+
Field field = null;
132+
Class<?> currentClass = this.packet.getClass();
133+
while (currentClass != null) {
134+
try {
135+
field = currentClass.getDeclaredField(key);
136+
break; // Field found, exit the loop
137+
} catch (NoSuchFieldException e) {
138+
// Field not in this class, move to the superclass
139+
currentClass = currentClass.getSuperclass();
140+
}
141+
}
98142

99-
// Remove the 'final' modifier
143+
if (field == null) {
144+
// Field does not exist in the class hierarchy, do nothing.
145+
return;
146+
}
147+
148+
// First, attempt the "old way" which might work on older Java versions or for non-final fields.
100149
try {
150+
field.setAccessible(true);
101151
Field modifiersField = Field.class.getDeclaredField("modifiers");
102152
modifiersField.setAccessible(true);
103153
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
104-
} catch (Exception e) {
105-
// Does not have modifiers, skip
154+
field.set(this.packet, value);
155+
} catch (Exception standardReflectionFailed) {
156+
// If the standard way fails, fall back to the extreme Unsafe bypass.
157+
// This is expected on modern Java versions (9+).
158+
long offset = unsafe.objectFieldOffset(field);
159+
160+
// Use the correct "put" method based on the value's type.
161+
if (value instanceof Boolean) {
162+
unsafe.putBoolean(this.packet, offset, (Boolean) value);
163+
} else if (value instanceof Byte) {
164+
unsafe.putByte(this.packet, offset, (Byte) value);
165+
} else if (value instanceof Short) {
166+
unsafe.putShort(this.packet, offset, (Short) value);
167+
} else if (value instanceof Integer) {
168+
unsafe.putInt(this.packet, offset, (Integer) value);
169+
} else if (value instanceof Long) {
170+
unsafe.putLong(this.packet, offset, (Long) value);
171+
} else if (value instanceof Float) {
172+
unsafe.putFloat(this.packet, offset, (Float) value);
173+
} else if (value instanceof Double) {
174+
unsafe.putDouble(this.packet, offset, (Double) value);
175+
} else if (value instanceof Character) {
176+
unsafe.putChar(this.packet, offset, (Character) value);
177+
} else {
178+
// For all other Object types
179+
unsafe.putObject(this.packet, offset, value);
180+
}
106181
}
107-
108-
field.setAccessible(true);
109-
field.set(packet, value);
110-
field.setAccessible(false);
111182
} catch (final Exception e) {
112-
e.printStackTrace();
183+
// Graceful: If any part of the process fails, the write operation is
184+
// aborted silently without crashing.
113185
}
114186
}
115187

116188
public void write(final String key, final ItemStack itemStack) {
117189
try {
118190
write(key, NMSItemStackConverter.convertToNMS(itemStack));
119191
} catch (final Exception e) {
120-
e.printStackTrace();
192+
// Graceful as requested
121193
}
122194
}
123195

@@ -184,4 +256,4 @@ public String getName() {
184256
public String toString() {
185257
return this.packet.toString();
186258
}
187-
}
259+
}

0 commit comments

Comments
 (0)