From ed4dc66f404ffbbc475a84febd3e807eeff51ffd Mon Sep 17 00:00:00 2001 From: Prof-Bloodstone <46570876+Prof-Bloodstone@users.noreply.github.com> Date: Thu, 16 Jul 2020 09:30:01 +0200 Subject: [PATCH] Make heads persistent, arbitrary object serializer (#4) * Fix typo in URL for WRENCH resource pack and DRY it During module registration, the URL had a trailing 0. * Add general PersistentDataType which uses GSON for arbitrary object serialization * Add PersistentHeads module, allowing for keeping name/lore after placing and breaking them --- .../vanillatweaks/VanillaTweaksModules.java | 3 + .../_managers/ModuleManager.java | 2 + .../persistentheads/PersistentHeads.java | 88 +++++++++++++++++++ .../utils/datatypes/JsonDataType.java | 42 +++++++++ .../vanillatweaks/wrenches/Wrench.java | 5 +- 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/main/java/me/machinemaker/vanillatweaks/persistentheads/PersistentHeads.java create mode 100644 src/main/java/me/machinemaker/vanillatweaks/utils/datatypes/JsonDataType.java diff --git a/src/main/java/me/machinemaker/vanillatweaks/VanillaTweaksModules.java b/src/main/java/me/machinemaker/vanillatweaks/VanillaTweaksModules.java index 0cabc057..95483cff 100644 --- a/src/main/java/me/machinemaker/vanillatweaks/VanillaTweaksModules.java +++ b/src/main/java/me/machinemaker/vanillatweaks/VanillaTweaksModules.java @@ -102,6 +102,9 @@ public class VanillaTweaksModules extends BaseConfig { @Path("utilities.real-time-clock") public Boolean realTimeClock = false; + @Path("utilities.persist-head-data") + public Boolean persistHeadData = false; + @Path("villagers.villagerdeathmessage") public Boolean villagerDeathMsgs = false; diff --git a/src/main/java/me/machinemaker/vanillatweaks/_managers/ModuleManager.java b/src/main/java/me/machinemaker/vanillatweaks/_managers/ModuleManager.java index 61505d59..42379707 100644 --- a/src/main/java/me/machinemaker/vanillatweaks/_managers/ModuleManager.java +++ b/src/main/java/me/machinemaker/vanillatweaks/_managers/ModuleManager.java @@ -15,6 +15,7 @@ import me.machinemaker.vanillatweaks.mobheads.MobHeads; import me.machinemaker.vanillatweaks.multiplayersleep.MultiplayerSleep; import me.machinemaker.vanillatweaks.netherportalcoords.NetherPortalCoords; +import me.machinemaker.vanillatweaks.persistentheads.PersistentHeads; import me.machinemaker.vanillatweaks.pillagertools.PillagerTools; import me.machinemaker.vanillatweaks.playergraves.PlayerGraves; import me.machinemaker.vanillatweaks.playerheaddrops.PlayerHeadDrops; @@ -90,6 +91,7 @@ public List allModules() { new PlayerHeadDrops(plugin), new TrackRawStats(plugin), new Tag(plugin), + new PersistentHeads(plugin), new VillagerDeathMessages(plugin), new PillagerTools(plugin), diff --git a/src/main/java/me/machinemaker/vanillatweaks/persistentheads/PersistentHeads.java b/src/main/java/me/machinemaker/vanillatweaks/persistentheads/PersistentHeads.java new file mode 100644 index 00000000..d8a5a45a --- /dev/null +++ b/src/main/java/me/machinemaker/vanillatweaks/persistentheads/PersistentHeads.java @@ -0,0 +1,88 @@ +package me.machinemaker.vanillatweaks.persistentheads; + +import io.papermc.lib.PaperLib; +import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult; +import me.machinemaker.vanillatweaks.BaseModule; +import me.machinemaker.vanillatweaks.VanillaTweaks; +import me.machinemaker.vanillatweaks.utils.datatypes.JsonDataType; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.TileState; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; + +public class PersistentHeads extends BaseModule implements Listener { + + private final NamespacedKey NAME_KEY = new NamespacedKey(plugin, "head_name"); + private final NamespacedKey LORE_KEY = new NamespacedKey(plugin, "head_lore"); + private final PersistentDataType LORE_PDT = new JsonDataType<>(String[].class); + + public PersistentHeads(VanillaTweaks plugin) { + super(plugin, config -> config.persistHeadData); + } + + @EventHandler + public void onBlockPlaceEvent(BlockPlaceEvent event) { + @NotNull ItemStack headItem = event.getItemInHand(); + if (headItem.getType() != Material.PLAYER_HEAD) return; + ItemMeta meta = headItem.getItemMeta(); + if (meta == null) return; + @NotNull String name = meta.getDisplayName(); + @Nullable List lore = meta.getLore(); + @NotNull Block block = event.getBlockPlaced(); + // NOTE: Not using snapshots is broken: https://github.com/PaperMC/Paper/issues/3913 + BlockStateSnapshotResult blockStateSnapshotResult = PaperLib.getBlockState(block, true); + TileState skullState = (TileState) blockStateSnapshotResult.getState(); + @NotNull PersistentDataContainer skullPDC = skullState.getPersistentDataContainer(); + skullPDC.set(NAME_KEY, PersistentDataType.STRING, name); + if (lore != null) skullPDC.set(LORE_KEY, LORE_PDT, lore.toArray(new String[0])); + if (blockStateSnapshotResult.isSnapshot()) skullState.update(); + } + + @EventHandler + public void onBlockDropItemEvent(BlockDropItemEvent event) { + @NotNull BlockState blockState = event.getBlockState(); + if (blockState.getType() != Material.PLAYER_HEAD) return; + TileState skullState = (TileState) blockState; + @NotNull PersistentDataContainer skullPDC = skullState.getPersistentDataContainer(); + @Nullable String name = skullPDC.get(NAME_KEY, PersistentDataType.STRING); + @Nullable String[] lore = skullPDC.get(LORE_KEY, LORE_PDT); + if (name == null) return; + for (Item item: event.getItems()) { // Ideally should only be one... + @NotNull ItemStack itemstack = item.getItemStack(); + if (itemstack.getType() == Material.PLAYER_HEAD) { + @Nullable ItemMeta meta = itemstack.getItemMeta(); + if (meta == null) continue; // This shouldn't happen + meta.setDisplayName(name); + if (lore != null) meta.setLore(Arrays.asList(lore)); + itemstack.setItemMeta(meta); + } + } + + } + + @Override + public void register() { + registerEvents(this); + } + + @Override + public void unregister() { + unregisterEvents(this); + } +} diff --git a/src/main/java/me/machinemaker/vanillatweaks/utils/datatypes/JsonDataType.java b/src/main/java/me/machinemaker/vanillatweaks/utils/datatypes/JsonDataType.java new file mode 100644 index 00000000..8cbe0988 --- /dev/null +++ b/src/main/java/me/machinemaker/vanillatweaks/utils/datatypes/JsonDataType.java @@ -0,0 +1,42 @@ +package me.machinemaker.vanillatweaks.utils.datatypes; + +import com.google.gson.Gson; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; + +/** + * Lets you store arbitrary data in PDC. + * @param + */ +public class JsonDataType implements PersistentDataType { + + private static final Gson gson = new Gson(); + private final Class typeClass; + + public JsonDataType(Class typeClass) { + this.typeClass = typeClass; + } + + @Override + public @NotNull Class getPrimitiveType() { + return String.class; + } + + @Override + public @NotNull Class getComplexType() { + return typeClass; + } + + @NotNull + @Override + public String toPrimitive(@NotNull T complex, @NotNull PersistentDataAdapterContext persistentDataAdapterContext) { + return gson.toJson(complex); + } + + @NotNull + @Override + public T fromPrimitive(@NotNull String primitive, @NotNull PersistentDataAdapterContext context) { + return gson.fromJson(primitive, getComplexType()); + } +} diff --git a/src/main/java/me/machinemaker/vanillatweaks/wrenches/Wrench.java b/src/main/java/me/machinemaker/vanillatweaks/wrenches/Wrench.java index a6d97077..83c32963 100644 --- a/src/main/java/me/machinemaker/vanillatweaks/wrenches/Wrench.java +++ b/src/main/java/me/machinemaker/vanillatweaks/wrenches/Wrench.java @@ -28,6 +28,7 @@ public class Wrench extends BaseModule implements Listener { + private final String resourcePackUrl = "https://potrebic.box.com/shared/static/uw4fvii2o8qsjuz6xuant1safwjdnrez.zip"; private final byte[] hash = new BigInteger("1ACF79C491B3CB9EEE50816AD0CC1FC45AABA147", 16).toByteArray(); private final NamespacedKey RECIPE_KEY = new NamespacedKey(this.plugin, "redstone_wrench"); private final List faces = Lists.newArrayList(BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.UP, BlockFace.DOWN); @@ -75,14 +76,14 @@ public void onPlayerInteract(PlayerInteractEvent event) { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { - event.getPlayer().setResourcePack("https://potrebic.box.com/shared/static/uw4fvii2o8qsjuz6xuant1safwjdnrez.zip", hash); + event.getPlayer().setResourcePack(resourcePackUrl, hash); } @Override public void register() { Bukkit.addRecipe(recipe); this.registerEvents(this); - Bukkit.getOnlinePlayers().forEach(player -> player.setResourcePack("https://potrebic.box.com/shared/static/uw4fvii2o8qsjuz6xuant1safwjdnrez.zip0", hash)); + Bukkit.getOnlinePlayers().forEach(player -> player.setResourcePack(resourcePackUrl, hash)); } @Override