diff --git a/src/main/java/com/onarandombox/multiverseinventories/share/SharableSerializer.java b/src/main/java/com/onarandombox/multiverseinventories/share/SharableSerializer.java index d3488fd9..86c34d11 100644 --- a/src/main/java/com/onarandombox/multiverseinventories/share/SharableSerializer.java +++ b/src/main/java/com/onarandombox/multiverseinventories/share/SharableSerializer.java @@ -8,8 +8,8 @@ public interface SharableSerializer { /** - * This deserializes the data for a Sharable. You must be expecting the type of data coming in in order to process - * this. That type will generally be the type that this serializes as with {@link #serialize(Object)}. + * This deserializes the data for a Sharable. You must be expecting the type of data coming in in order to process + * this. That type will generally be the type that this serializes as with {@link #serialize(Object)}. * * @param obj The incoming (serialized) data to be deserialized. * @return The data represented by the Sharable this object represents in deserialized form. @@ -17,9 +17,9 @@ public interface SharableSerializer { T deserialize(Object obj); /** - * This serializes the data for a Sharable. The output is an Object but what you return is up to you, however, - * this is limited by the constraints of the persistence method. Generally, returning a String is the safest way - * to serialize your data. Most boxed primitives are accepted as well as Lists of boxed primitives and + * This serializes the data for a Sharable. The output is an Object but what you return is up to you, however, + * this is limited by the constraints of the persistence method. Generally, returning a String is the safest way + * to serialize your data. Most boxed primitives are accepted as well as Lists of boxed primitives and * Map. * * @param t The value of the data represented by the Sharable. diff --git a/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java b/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java index 1a3702d6..3a177526 100644 --- a/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java +++ b/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java @@ -7,11 +7,8 @@ import com.onarandombox.multiverseinventories.PlayerStats; import com.onarandombox.multiverseinventories.profile.PlayerProfile; import com.onarandombox.multiverseinventories.util.MinecraftTools; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.advancement.Advancement; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataAdapterContext; @@ -20,17 +17,9 @@ import org.bukkit.potion.PotionEffect; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Array; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * The Sharables class is where all the default Sharable instances are located as constants as well as a factory class @@ -688,6 +677,98 @@ public static void setPlayerSpawnLocation(Player player, Location spawnLocation) player.getPersistentDataContainer().set(SPAWN_LOCATION_KEY, LOCATION_TAG_TYPE, spawnLocation); } + /** + * Sharing Advancements. + */ + public static final Sharable ADVANCEMENTS = new Sharable.Builder("advancements", String[].class, + new SharableHandler() { + @Override + public void updateProfile(PlayerProfile profile, Player player) { + HashSet completedAdvancements = new HashSet<>(); + Iterator advancementIterator = inventories.getServer().advancementIterator(); + + while (advancementIterator.hasNext()) { + Advancement advancement = advancementIterator.next(); + Collection awardedCriteria = player.getAdvancementProgress(advancement).getAwardedCriteria(); + completedAdvancements.addAll(awardedCriteria); + } + + profile.set(ADVANCEMENTS, completedAdvancements.toArray(new String[completedAdvancements.size()])); + } + + @Override + public boolean updatePlayer(Player player, PlayerProfile profile) { + String[] advancements = profile.get(ADVANCEMENTS); + HashSet processedAdvancements = new HashSet<>(); + HashSet completedAdvancements = (advancements != null) ? new HashSet<>(Arrays.asList(advancements)) : new HashSet<>(); + Iterator advancementIterator = inventories.getServer().advancementIterator(); + + while (advancementIterator.hasNext()) { + Advancement advancement = advancementIterator.next(); + // the set of Advancements to revoke + Set revoke = new HashSet<>(advancement.getCriteria()); + // the set of Advancements to grant + Set intersection = new HashSet<>(advancement.getCriteria()); + + revoke.removeAll(processedAdvancements); + revoke.removeAll(completedAdvancements); + intersection.removeAll(processedAdvancements); + intersection.retainAll(completedAdvancements); + + for (String criteria: revoke) { + processedAdvancements.add(criteria); + completedAdvancements.remove(criteria); + player.getAdvancementProgress(advancement).revokeCriteria(criteria); + } + for (String criteria: intersection) { + processedAdvancements.add(criteria); + completedAdvancements.remove(criteria); + player.getAdvancementProgress(advancement).awardCriteria(criteria); + } + + // here's the idea: revoke all (criteria \ completedAdvancements), grant (criteria intersect completedAdvancements) + // also, we don't need to grant/revoke anything in processedAdvancements since they've already been granted/revoked! + } + return advancements != null; + } + }).serializer(new ProfileEntry(false, "advancements"), new StringArraySerializer()) + .altName("achievements").build(); + + /** + * Sharing Statistics. + */ + public static final Sharable GAME_STATISTICS = new Sharable.Builder("game_statistics", HashMap.class, + new SharableHandler() { + @Override + public void updateProfile(PlayerProfile profile, Player player) { + HashMap playerStats = new HashMap<>(); + for (Statistic stat: Statistic.values()) { + if (stat.getType() == Statistic.Type.UNTYPED) { + int val = player.getStatistic(stat); + // no need to save values of 0, that's the default! + if (val != 0) playerStats.put(stat.name(), val); + } + } + profile.set(GAME_STATISTICS, playerStats); + } + + @Override + public boolean updatePlayer(Player player, PlayerProfile profile) { + HashMap playerStats = profile.get(GAME_STATISTICS); + for (Statistic stat : Statistic.values()) { + if (stat.getType() == Statistic.Type.UNTYPED) player.setStatistic(stat, 0); + } + if (playerStats == null) { + return false; + } + for (String stringStat : playerStats.keySet()) { + Statistic stat = Statistic.valueOf(stringStat); + if (stat.getType() == Statistic.Type.UNTYPED) player.setStatistic(stat, playerStats.get(stat.name())); + } + return true; + } + }).defaultSerializer(new ProfileEntry(false, "game_statistics")).altName("game_stats").build(); + /** * Grouping for inventory sharables. */ @@ -731,7 +812,8 @@ public static void setPlayerSpawnLocation(Player player, Location spawnLocation) */ public static final SharableGroup ALL_DEFAULT = new SharableGroup("all", fromSharables(HEALTH, ECONOMY, FOOD_LEVEL, SATURATION, EXHAUSTION, EXPERIENCE, TOTAL_EXPERIENCE, LEVEL, INVENTORY, ARMOR, BED_SPAWN, - MAXIMUM_AIR, REMAINING_AIR, FALL_DISTANCE, FIRE_TICKS, POTIONS, LAST_LOCATION, ENDER_CHEST, OFF_HAND), + MAXIMUM_AIR, REMAINING_AIR, FALL_DISTANCE, FIRE_TICKS, POTIONS, LAST_LOCATION, ENDER_CHEST, OFF_HAND, + ADVANCEMENTS, GAME_STATISTICS), "*", "everything"); diff --git a/src/main/java/com/onarandombox/multiverseinventories/share/StringArraySerializer.java b/src/main/java/com/onarandombox/multiverseinventories/share/StringArraySerializer.java new file mode 100644 index 00000000..b71d58d3 --- /dev/null +++ b/src/main/java/com/onarandombox/multiverseinventories/share/StringArraySerializer.java @@ -0,0 +1,24 @@ +package com.onarandombox.multiverseinventories.share; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StringArraySerializer implements SharableSerializer { + @Override + public String[] deserialize(Object obj) { + List list = (List) obj; + List resultList = new ArrayList<>(list.size()); + for (Object o : list) { + if (o instanceof String) { + resultList.add((String) o); + } + } + return resultList.toArray(new String[resultList.size()]); + } + + @Override + public Object serialize(String[] strings) { + return Arrays.asList(strings); + } +}