mirror of
https://github.com/EssentialsX/Essentials.git
synced 2025-01-03 06:57:39 +01:00
Add ItemStack 1.8.8+ cross-version PersistentDataContainer (#4143)
This PR itself does nothing on its own but creates the underlying backbone I need to make a less hacky solution in #3963 lmfao. This PR creates a provider which uses NBT on 1.8.8-1.13 to mimic the exact structure of a PersistentDataContainer on 1.14+ which will allow us make any possible upgrades (which don't die from the lack of DFU on >1.13) work as expected. Additionally, this does not use reflection on modern Minecraft versions and thus will not need to be maintained/updated on MC version updates. In the future, we will need to find a way to store data on tile entities (signs namely) so that we are able to store UUIDs on signs for future plans, but for now ItemStacks work to fix our spawner issues.
This commit is contained in:
parent
4ed36d9bbe
commit
ec50b28f4b
@ -47,6 +47,7 @@ import net.ess3.api.IJails;
|
||||
import net.ess3.api.ISettings;
|
||||
import net.ess3.nms.refl.providers.ReflFormattedCommandAliasProvider;
|
||||
import net.ess3.nms.refl.providers.ReflKnownCommandsProvider;
|
||||
import net.ess3.nms.refl.providers.ReflPersistentDataProvider;
|
||||
import net.ess3.nms.refl.providers.ReflServerStateProvider;
|
||||
import net.ess3.nms.refl.providers.ReflSpawnEggProvider;
|
||||
import net.ess3.nms.refl.providers.ReflSpawnerBlockProvider;
|
||||
@ -55,6 +56,7 @@ import net.ess3.provider.ContainerProvider;
|
||||
import net.ess3.provider.FormattedCommandAliasProvider;
|
||||
import net.ess3.provider.KnownCommandsProvider;
|
||||
import net.ess3.provider.MaterialTagProvider;
|
||||
import net.ess3.provider.PersistentDataProvider;
|
||||
import net.ess3.provider.PotionMetaProvider;
|
||||
import net.ess3.provider.ProviderListener;
|
||||
import net.ess3.provider.ServerStateProvider;
|
||||
@ -69,6 +71,7 @@ import net.ess3.provider.providers.BukkitSpawnerBlockProvider;
|
||||
import net.ess3.provider.providers.FlatSpawnEggProvider;
|
||||
import net.ess3.provider.providers.LegacyPotionMetaProvider;
|
||||
import net.ess3.provider.providers.LegacySpawnEggProvider;
|
||||
import net.ess3.provider.providers.ModernPersistentDataProvider;
|
||||
import net.ess3.provider.providers.PaperContainerProvider;
|
||||
import net.ess3.provider.providers.PaperKnownCommandsProvider;
|
||||
import net.ess3.provider.providers.PaperMaterialTagProvider;
|
||||
@ -154,6 +157,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
private transient ProviderListener recipeBookEventProvider;
|
||||
private transient MaterialTagProvider materialTagProvider;
|
||||
private transient SyncCommandsProvider syncCommandsProvider;
|
||||
private transient PersistentDataProvider persistentDataProvider;
|
||||
private transient Kits kits;
|
||||
private transient RandomTeleport randomTeleport;
|
||||
private transient UpdateChecker updateChecker;
|
||||
@ -383,6 +387,12 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
// Sync Commands Provider
|
||||
syncCommandsProvider = new ReflSyncCommandsProvider();
|
||||
|
||||
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_14_4_R01)) {
|
||||
persistentDataProvider = new ModernPersistentDataProvider(this);
|
||||
} else {
|
||||
persistentDataProvider = new ReflPersistentDataProvider(this);
|
||||
}
|
||||
|
||||
execTimer.mark("Init(Providers)");
|
||||
reload();
|
||||
|
||||
@ -1143,6 +1153,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
return syncCommandsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentDataProvider getPersistentDataProvider() {
|
||||
return persistentDataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginCommand getPluginCommand(final String cmd) {
|
||||
return this.getCommand(cmd);
|
||||
|
@ -6,10 +6,11 @@ import com.earth2me.essentials.api.IWarps;
|
||||
import com.earth2me.essentials.commands.IEssentialsCommand;
|
||||
import com.earth2me.essentials.perm.PermissionsHandler;
|
||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||
import net.ess3.provider.MaterialTagProvider;
|
||||
import net.ess3.provider.ContainerProvider;
|
||||
import net.ess3.provider.FormattedCommandAliasProvider;
|
||||
import net.ess3.provider.KnownCommandsProvider;
|
||||
import net.ess3.provider.MaterialTagProvider;
|
||||
import net.ess3.provider.PersistentDataProvider;
|
||||
import net.ess3.provider.ServerStateProvider;
|
||||
import net.ess3.provider.SpawnerBlockProvider;
|
||||
import net.ess3.provider.SpawnerItemProvider;
|
||||
@ -139,5 +140,7 @@ public interface IEssentials extends Plugin {
|
||||
|
||||
SyncCommandsProvider getSyncCommandsProvider();
|
||||
|
||||
PersistentDataProvider getPersistentDataProvider();
|
||||
|
||||
PluginCommand getPluginCommand(String cmd);
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package net.ess3.provider;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface PersistentDataProvider extends Provider {
|
||||
void set(ItemStack itemStack, String key, String value);
|
||||
|
||||
String getString(ItemStack itemStack, String key);
|
||||
|
||||
void remove(ItemStack itemStack, String key);
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package net.ess3.provider.providers;
|
||||
|
||||
import net.ess3.provider.PersistentDataProvider;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class ModernPersistentDataProvider implements PersistentDataProvider {
|
||||
private final Plugin plugin;
|
||||
|
||||
public ModernPersistentDataProvider(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack, String key, String value) {
|
||||
if (itemStack == null || itemStack.getItemMeta() == null || key == null || value == null) {
|
||||
return;
|
||||
}
|
||||
final ItemMeta im = itemStack.getItemMeta();
|
||||
|
||||
im.getPersistentDataContainer().set(new NamespacedKey(plugin, key), PersistentDataType.STRING, value);
|
||||
itemStack.setItemMeta(im);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(ItemStack itemStack, String key) {
|
||||
if (itemStack == null || itemStack.getItemMeta() == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return itemStack.getItemMeta().getPersistentDataContainer().get(new NamespacedKey(plugin, key), PersistentDataType.STRING);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ItemStack itemStack, String key) {
|
||||
itemStack.getItemMeta().getPersistentDataContainer().remove(new NamespacedKey(plugin, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "1.14+ Persistent Data Container Provider";
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package net.ess3.nms.refl.providers;
|
||||
|
||||
import net.ess3.nms.refl.ReflUtil;
|
||||
import net.ess3.provider.PersistentDataProvider;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Stores persistent data on 1.18-1.13 in a manner that's consistent with PDC on 1.14+ to enable
|
||||
* seamless upgrades.
|
||||
*/
|
||||
public class ReflPersistentDataProvider implements PersistentDataProvider {
|
||||
private static final String PDC_ROOT_TAG = "PublicBukkitValues";
|
||||
private static final String ROOT_TAG = "tag";
|
||||
private final String namespace;
|
||||
private final MethodHandle itemHandleGetterHandle;
|
||||
private final MethodHandle getTagHandle;
|
||||
private final MethodHandle tagSetterHandle;
|
||||
private final MethodHandle newCompoundHandle;
|
||||
private final MethodHandle getCompoundHandle;
|
||||
private final MethodHandle setCompoundHandle;
|
||||
private final MethodHandle setStringHandle;
|
||||
private final MethodHandle removeHandle;
|
||||
private final MethodHandle getStringHandle;
|
||||
|
||||
public ReflPersistentDataProvider(Plugin plugin) {
|
||||
this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
|
||||
|
||||
MethodHandle itemHandleGetterHandle = null;
|
||||
MethodHandle getTagHandle = null;
|
||||
MethodHandle tagSetterHandle = null;
|
||||
MethodHandle newCompoundHandle = null;
|
||||
MethodHandle getCompoundHandle = null;
|
||||
MethodHandle setCompoundHandle = null;
|
||||
MethodHandle setStringHandle = null;
|
||||
MethodHandle removeHandle = null;
|
||||
MethodHandle getStringHandle = null;
|
||||
try {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
final Field handleGetter = ReflUtil.getOBCClass("inventory.CraftItemStack").getDeclaredField("handle");
|
||||
handleGetter.setAccessible(true);
|
||||
itemHandleGetterHandle = lookup.unreflectGetter(handleGetter);
|
||||
final Field tagSetter = ReflUtil.getNMSClass("ItemStack").getDeclaredField("tag");
|
||||
tagSetter.setAccessible(true);
|
||||
tagSetterHandle = lookup.unreflectSetter(tagSetter);
|
||||
getTagHandle = lookup.findVirtual(ReflUtil.getNMSClass("ItemStack"), "getTag", MethodType.methodType(ReflUtil.getNMSClass("NBTTagCompound")));
|
||||
newCompoundHandle = lookup.findConstructor(ReflUtil.getNMSClass("NBTTagCompound"), MethodType.methodType(void.class));
|
||||
getCompoundHandle = lookup.findVirtual(ReflUtil.getNMSClass("NBTTagCompound"), "getCompound", MethodType.methodType(ReflUtil.getNMSClass("NBTTagCompound"), String.class));
|
||||
setCompoundHandle = lookup.findVirtual(ReflUtil.getNMSClass("NBTTagCompound"), "set", MethodType.methodType(void.class, String.class, ReflUtil.getNMSClass("NBTBase")));
|
||||
setStringHandle = lookup.findVirtual(ReflUtil.getNMSClass("NBTTagCompound"), "setString", MethodType.methodType(void.class, String.class, String.class));
|
||||
removeHandle = lookup.findVirtual(ReflUtil.getNMSClass("NBTTagCompound"), "remove", MethodType.methodType(void.class, String.class));
|
||||
getStringHandle = lookup.findVirtual(ReflUtil.getNMSClass("NBTTagCompound"), "getString", MethodType.methodType(String.class, String.class));
|
||||
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.itemHandleGetterHandle = itemHandleGetterHandle;
|
||||
this.getTagHandle = getTagHandle;
|
||||
this.tagSetterHandle = tagSetterHandle;
|
||||
this.newCompoundHandle = newCompoundHandle;
|
||||
this.getCompoundHandle = getCompoundHandle;
|
||||
this.setCompoundHandle = setCompoundHandle;
|
||||
this.setStringHandle = setStringHandle;
|
||||
this.removeHandle = removeHandle;
|
||||
this.getStringHandle = getStringHandle;
|
||||
}
|
||||
|
||||
private String getPersistentString(ItemStack itemStack, String key) throws Throwable {
|
||||
final Object nmsItem = itemHandleGetterHandle.invoke(itemStack);
|
||||
final Object itemRootTag = getTagHandle.invoke(nmsItem);
|
||||
if (itemRootTag == null) {
|
||||
return null;
|
||||
}
|
||||
final Object tagCompound = getCompoundHandle.invoke(itemRootTag, ROOT_TAG);
|
||||
final Object publicBukkitValuesCompound = getCompoundHandle.invoke(tagCompound, PDC_ROOT_TAG);
|
||||
return (String) getStringHandle.invoke(publicBukkitValuesCompound, key);
|
||||
}
|
||||
|
||||
private void setPersistentString(ItemStack itemStack, String key, String value) throws Throwable {
|
||||
final Object nmsItem = itemHandleGetterHandle.invoke(itemStack);
|
||||
Object itemRootTag = getTagHandle.invoke(nmsItem);
|
||||
if (itemRootTag == null) {
|
||||
itemRootTag = newCompoundHandle.invoke();
|
||||
tagSetterHandle.invoke(nmsItem, itemRootTag);
|
||||
}
|
||||
final Object tagCompound = getCompoundHandle.invoke(itemRootTag, ROOT_TAG);
|
||||
final Object publicBukkitValuesCompound = getCompoundHandle.invoke(tagCompound, PDC_ROOT_TAG);
|
||||
if (value == null) {
|
||||
removeHandle.invoke(publicBukkitValuesCompound, key);
|
||||
} else {
|
||||
setStringHandle.invoke(publicBukkitValuesCompound, key, value);
|
||||
}
|
||||
setCompoundHandle.invoke(tagCompound, PDC_ROOT_TAG, publicBukkitValuesCompound);
|
||||
setCompoundHandle.invoke(itemRootTag, ROOT_TAG, tagCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack, String key, String value) {
|
||||
try {
|
||||
setPersistentString(itemStack, namespace + ":" + key, value);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(ItemStack itemStack, String key) {
|
||||
try {
|
||||
return getPersistentString(itemStack, namespace + ":" + key);
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ItemStack itemStack, String key) {
|
||||
try {
|
||||
setPersistentString(itemStack, namespace + ":" + key, null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "1.13 >= Persistent Data Container Provider";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user