Reopen inventory for refreshing galacitc text rather than using reflection

This commit is contained in:
NotAlexNoyle committed 2026-04-06 04:48:24 -04:00
1 parent efc4a3ecd2
commit 6f4c824f98
1 file changed
+44 -128
@@ -5,9 +5,6 @@
*/
package net.trueog.oldenchantog;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -16,6 +13,7 @@ import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentOffer;
@@ -53,17 +51,7 @@ public class OldEnchantOG extends JavaPlugin implements Listener {
// Per-player storage for 1.7.10-style enchantment offers.
private final Map<UUID, int[]> playerCosts = new HashMap<>();
private final Map<UUID, List<Map<Enchantment, Integer>>> playerEnchantments = new HashMap<>();
// Reflection handles for syncing the enchantment seed DataSlot to the client.
// The client renders galactic text based on the seed in EnchantmentMenu's
// DataSlot,
// which is NOT updated by HumanEntity.setEnchantmentSeed() alone.
private Field nmsContainerField;
private Class<?> dataSlotClass;
private Method dataSlotSetMethod;
private Field enchantSeedField;
private boolean reflectionInitialized;
private boolean reflectionAvailable;
private final Map<UUID, Location> playerEnchantingLocations = new HashMap<>();
@Override
public void onEnable() {
@@ -144,6 +132,7 @@ public class OldEnchantOG extends JavaPlugin implements Listener {
final UUID playerId = event.getPlayer().getUniqueId();
playerCosts.remove(playerId);
playerEnchantments.remove(playerId);
playerEnchantingLocations.remove(playerId);
}
@@ -156,6 +145,9 @@ public class OldEnchantOG extends JavaPlugin implements Listener {
final ItemStack item = event.getItem();
final int bookshelves = event.getEnchantmentBonus();
// Track the enchanting table block location for container reopening.
playerEnchantingLocations.put(playerId, event.getEnchantBlock().getLocation());
// Calculate 1.7.10 costs based on bookshelf count.
final int[] costs = OldEnchantAlgorithm.calculateCosts(bookshelves);
playerCosts.put(playerId, costs);
@@ -299,15 +291,47 @@ public class OldEnchantOG extends JavaPlugin implements Listener {
final EnchantingInventory enchantingInventory = (EnchantingInventory) inventory;
final ItemStack item = enchantingInventory.getItem(ENCHANTING_ITEM_SLOT);
if (item == null || item.getType() == Material.AIR) {
shuffleEnchantingOffers(humanEntity);
fillUpEnchantingTable(enchantingInventory);
return;
if (item != null && item.getType() != Material.AIR) {
}
// Re-applying the item forces Bukkit to rebuild the full offer list with the
// new seed.
enchantingInventory.setItem(ENCHANTING_ITEM_SLOT, item.clone());
final UUID playerId = humanEntity.getUniqueId();
final Location tableLocation = playerEnchantingLocations.get(playerId);
if (tableLocation == null) {
return;
}
// Save the item, then clear both slots before closing to prevent
// the server from returning them to the player's inventory.
final ItemStack savedItem = item.clone();
enchantingInventory.setItem(ENCHANTING_ITEM_SLOT, null);
enchantingInventory.setSecondary(null);
// Set a new enchantment seed. The new EnchantmentMenu created by
// openEnchanting reads this seed into its DataSlot, so the client
// will render fresh galactic text.
humanEntity.setEnchantmentSeed(ThreadLocalRandom.current().nextInt());
// Reopen the enchanting table (internally closes the old container).
humanEntity.openEnchanting(tableLocation, true);
// Restore lapis and the item in the new container.
// setItem triggers PrepareItemEnchantEvent which regenerates our
// custom 1.7.10 offers.
final Inventory newInventory = humanEntity.getOpenInventory().getTopInventory();
if (newInventory.getType() == InventoryType.ENCHANTING) {
fillUpEnchantingTable(newInventory);
((EnchantingInventory) newInventory).setItem(ENCHANTING_ITEM_SLOT, savedItem);
} else {
// Reopen failed; give the item back.
humanEntity.getInventory().addItem(savedItem);
}
@@ -315,114 +339,6 @@ public class OldEnchantOG extends JavaPlugin implements Listener {
}
private void shuffleEnchantingOffers(HumanEntity humanEntity) {
final int newSeed = ThreadLocalRandom.current().nextInt();
humanEntity.setEnchantmentSeed(newSeed);
// Also update the EnchantmentMenu's DataSlot so the client re-renders
// the galactic text. Without this, the DataSlot retains the old seed
// from when the container was first opened.
syncEnchantmentSeedToClient(humanEntity, newSeed);
}
private void syncEnchantmentSeedToClient(HumanEntity humanEntity, int seed) {
if (!ensureReflection(humanEntity)) {
return;
}
try {
final Object nmsMenu = nmsContainerField.get(humanEntity.getOpenInventory());
final Object dataSlot = enchantSeedField.get(nmsMenu);
dataSlotSetMethod.invoke(dataSlot, seed);
} catch (Exception e) {
getLogger().warning("Failed to sync enchantment seed: " + e.getMessage());
}
}
private boolean ensureReflection(HumanEntity humanEntity) {
if (reflectionInitialized) {
return reflectionAvailable;
}
try {
// CraftInventoryView has a "container" field holding the NMS menu.
final Object view = humanEntity.getOpenInventory();
nmsContainerField = view.getClass().getDeclaredField("container");
nmsContainerField.setAccessible(true);
// DataSlot class (Mojang-mapped name, stable since Spigot 1.17+).
dataSlotClass = Class.forName("net.minecraft.world.inventory.DataSlot");
// Find void set(int) on DataSlot (name is obfuscated, match by signature).
for (Method m : dataSlotClass.getDeclaredMethods()) {
if (!Modifier.isStatic(m.getModifiers()) && m.getParameterCount() == 1
&& m.getParameterTypes()[0] == int.class && m.getReturnType() == void.class)
{
m.setAccessible(true);
dataSlotSetMethod = m;
break;
}
}
if (dataSlotSetMethod == null) {
throw new NoSuchMethodException("Could not find DataSlot.set(int)");
}
// The first DataSlot-typed field on EnchantmentMenu is enchantmentSeed.
final Object nmsMenu = nmsContainerField.get(view);
for (Field f : nmsMenu.getClass().getDeclaredFields()) {
if (dataSlotClass.isAssignableFrom(f.getType())) {
f.setAccessible(true);
enchantSeedField = f;
break;
}
}
if (enchantSeedField == null) {
throw new NoSuchFieldException("Could not find DataSlot field on enchantment menu");
}
reflectionInitialized = true;
reflectionAvailable = true;
return true;
} catch (Exception e) {
reflectionInitialized = true;
reflectionAvailable = false;
getLogger().warning("Could not initialize enchantment seed sync: " + e.getMessage());
getLogger().warning("Galactic text in the enchanting table will not update when items are touched.");
return false;
}
}
private ItemStack getLapis() {
return new ItemStack(Material.LAPIS_LAZULI, AUTO_LAPIS_AMOUNT);