Reopen inventory for refreshing galacitc text rather than using reflection
This commit is contained in:
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);
|
||||
|
||||
Reference in new issue
Block a user