diff --git a/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java b/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java index 8de6d01a..11e4cf42 100644 --- a/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java +++ b/src/main/java/com/onarandombox/multiverseinventories/share/Sharables.java @@ -8,19 +8,27 @@ import com.onarandombox.multiverseinventories.profile.PlayerProfile; import com.onarandombox.multiverseinventories.util.MinecraftTools; import org.bukkit.Bukkit; +import org.bukkit.GameRule; +import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; +import org.bukkit.NamespacedKey; +import org.bukkit.Statistic; +import org.bukkit.advancement.Advancement; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; import org.bukkit.potion.PotionEffect; import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -582,6 +590,166 @@ public boolean updatePlayer(Player player, PlayerProfile profile) { }).serializer(new ProfileEntry(false, "potions"), new PotionEffectSerializer()) .altName("potion").altName("potions").build(); + /** + * Sharing Advancements. + */ + public static final Sharable ADVANCEMENTS = new Sharable.Builder("advancements", List.class, + new SharableHandler() { + @Override + public void updateProfile(PlayerProfile profile, Player player) { + Set 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, new ArrayList<>(completedAdvancements)); + } + + @Override + public boolean updatePlayer(Player player, PlayerProfile profile) { + List advancements = profile.get(ADVANCEMENTS); + Set processedCriteria = new HashSet<>(); + Set completedCriteria = (advancements != null) ? new HashSet<>(advancements) : new HashSet<>(); + + int totalExperience = player.getTotalExperience(); + int level = player.getLevel(); + float exp = player.getExp(); + + boolean announceAdvancements = player.getWorld().getGameRuleValue(GameRule.ANNOUNCE_ADVANCEMENTS); + if (announceAdvancements) { + player.getWorld().setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false); + } + + Iterator advancementIterator = inventories.getServer().advancementIterator(); + while (advancementIterator.hasNext()) { + Advancement advancement = advancementIterator.next(); + + for (String criteria : advancement.getCriteria()) { + if (processedCriteria.contains(criteria)) { + continue; + } else if (completedCriteria.contains(criteria)) { + player.getAdvancementProgress(advancement).awardCriteria(criteria); + } else { + player.getAdvancementProgress(advancement).revokeCriteria(criteria); + } + + processedCriteria.add(criteria); + } + } + + player.setExp(exp); + player.setLevel(level); + player.setTotalExperience(totalExperience); + + if (announceAdvancements) { + player.getWorld().setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, true); + } + + return advancements != null; + } + }).defaultSerializer(new ProfileEntry(false, "advancements")).altName("achievements").build(); + + /** + * Sharing Statistics. + */ + public static final Sharable GAME_STATISTICS = new Sharable.Builder("game_statistics", Map.class, + new SharableHandler() { + @Override + public void updateProfile(PlayerProfile profile, Player player) { + Map 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) { + Map 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 (Map.Entry statInfo : playerStats.entrySet()) { + Statistic stat = Statistic.valueOf(statInfo.getKey()); + if (stat.getType() == Statistic.Type.UNTYPED) { + player.setStatistic(stat, statInfo.getValue()); + } + } + + return true; + } + }).defaultSerializer(new ProfileEntry(false, "game_statistics")).altName("game_stats").build(); + + /** + * Sharing Recipes. + */ + public static final Sharable RECIPES = new Sharable.Builder("recipes", Map.class, + new SharableHandler() { + @Override + public void updateProfile(PlayerProfile profile, Player player) { + Set recipes = new HashSet<>(); + Map> recipesMap = new HashMap<>(); + Iterator recipeIterator = inventories.getServer().recipeIterator(); + + while (recipeIterator.hasNext()) { + Recipe recipe = recipeIterator.next(); + if (recipe instanceof Keyed) { + NamespacedKey key = ((Keyed) recipe).getKey(); + if (player.undiscoverRecipe(key)) { + recipes.add(key); + recipesMap.putIfAbsent(key.getNamespace(), new ArrayList<>()); + recipesMap.get(key.getNamespace()).add(key.getKey()); + } + } + } + + player.discoverRecipes(recipes); + profile.set(RECIPES, recipesMap); + } + + @Override + public boolean updatePlayer(Player player, PlayerProfile profile) { + Map> recipes = profile.get(RECIPES); + if (recipes == null) { + recipes = new HashMap<>(); + } + + Iterator recipeIterator = inventories.getServer().recipeIterator(); + while (recipeIterator.hasNext()) { + Recipe recipe = recipeIterator.next(); + if (recipe instanceof Keyed) { + NamespacedKey key = ((Keyed) recipe).getKey(); + List namespace = recipes.get(key.getNamespace()); + if (namespace != null && namespace.contains(key.getKey())) { + player.discoverRecipe(key); + } else { + player.undiscoverRecipe(key); + } + } + } + + return !recipes.isEmpty(); + } + }).defaultSerializer(new ProfileEntry(false, "recipes")).build(); + /** * Grouping for inventory sharables. */ @@ -617,7 +785,7 @@ public boolean updatePlayer(Player player, PlayerProfile profile) { */ public static final SharableGroup STATS = new SharableGroup("stats", fromSharables(HEALTH, FOOD_LEVEL, SATURATION, EXHAUSTION, EXPERIENCE, TOTAL_EXPERIENCE, LEVEL, - REMAINING_AIR, MAXIMUM_AIR, FALL_DISTANCE, FIRE_TICKS, POTIONS)); + REMAINING_AIR, MAXIMUM_AIR, FALL_DISTANCE, FIRE_TICKS, POTIONS, GAME_STATISTICS)); /** * Grouping for ALL default sharables. @@ -625,7 +793,8 @@ public boolean updatePlayer(Player player, PlayerProfile profile) { */ 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, RECIPES), "*", "everything");