Skip to content

Commit

Permalink
Make heads persistent, arbitrary object serializer (#4)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Prof-Bloodstone authored Jul 16, 2020
1 parent b25fd38 commit ed4dc66
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -90,6 +91,7 @@ public List<BaseModule> allModules() {
new PlayerHeadDrops(plugin),
new TrackRawStats(plugin),
new Tag(plugin),
new PersistentHeads(plugin),

new VillagerDeathMessages(plugin),
new PillagerTools(plugin),
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String,String[]> 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<String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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 <T>
*/
public class JsonDataType<T> implements PersistentDataType<String, T> {

private static final Gson gson = new Gson();
private final Class<T> typeClass;

public JsonDataType(Class<T> typeClass) {
this.typeClass = typeClass;
}

@Override
public @NotNull Class<String> getPrimitiveType() {
return String.class;
}

@Override
public @NotNull Class<T> 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockFace> faces = Lists.newArrayList(BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.UP, BlockFace.DOWN);
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ed4dc66

Please sign in to comment.