/* * This file is part of HeavySpleef. * Copyright (c) 2014-2016 Matthias Werning * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.xaniox.heavyspleef.core.player; import com.google.common.collect.Lists; import de.xaniox.heavyspleef.core.MinecraftVersion; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.potion.PotionEffect; import org.bukkit.scoreboard.Scoreboard; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; public class PlayerStateHolder { /* Saving the inventory and the armor contents */ private static final int SIMPLE_INVENTORY_SIZE = 4 * 9; private static final int ARMOR_INVENTORY_SIZE = 4; private static final int INVENTORY_SIZE = SIMPLE_INVENTORY_SIZE + ARMOR_INVENTORY_SIZE; private ItemStack[] inventory; private ItemStack onCursor; private GameMode gamemode; private double maxHealth; private double health; private int foodLevel; private int level; private float experience; private boolean allowFlight; private boolean isFlying; private Collection<PotionEffect> activeEffects; private float exhaustion; private float saturation; private float fallDistance; private int fireTicks; private List<WeakReference<Player>> cantSee; private Scoreboard scoreboard; private Location compassTarget; private Location location; public PlayerStateHolder() {} public static PlayerStateHolder create(Player player, GameMode gameMode) { PlayerStateHolder stateHolder = new PlayerStateHolder(); stateHolder.updateState(player, true, gameMode); return stateHolder; } /** * Applies the default state to the player * and discards the current one<br><br> * * Warning: This deletes the entire inventory * and all other various player attributes * * It is recommended to save the player state * with {@link PlayerStateHolder#create(Player, GameMode)} * and store a reference to it before invoking this method * * @param player */ public static void applyDefaultState(Player player, boolean adventureMode) { player.setGameMode(adventureMode ? GameMode.ADVENTURE : GameMode.SURVIVAL); player.getInventory().clear(); player.getInventory().setArmorContents(new ItemStack[4]); player.setItemOnCursor(null); player.updateInventory(); player.setMaxHealth(20.0); player.setHealth(20.0); player.setFoodLevel(20); player.setLevel(0); player.setExp(0f); player.setAllowFlight(false); player.setFlying(false); player.setFallDistance(0); player.setFireTicks(0); Collection<PotionEffect> effects = player.getActivePotionEffects(); for (PotionEffect effect : effects) { player.removePotionEffect(effect.getType()); } for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { if (player.canSee(player)) { continue; } player.showPlayer(onlinePlayer); } } public void apply(Player player) { apply(player, true); } public void apply(Player player, boolean teleport) { PlayerInventory playerInv = player.getInventory(); boolean is1_9 = MinecraftVersion.getImplementationVersion().compareTo(MinecraftVersion.V1_9) >= 0; boolean isSimpleSize = playerInv.getContents().length <= SIMPLE_INVENTORY_SIZE; ItemStack[] inventoryContents = new ItemStack[is1_9 && !isSimpleSize ? playerInv.getSize() : SIMPLE_INVENTORY_SIZE]; System.arraycopy(inventory, 0, inventoryContents, 0, inventoryContents.length); if (!is1_9 || isSimpleSize) { ItemStack[] armorContents = new ItemStack[ARMOR_INVENTORY_SIZE]; System.arraycopy(inventory, inventory.length - ARMOR_INVENTORY_SIZE, armorContents, 0, armorContents.length); playerInv.setArmorContents(armorContents); } playerInv.setContents(inventoryContents); player.setItemOnCursor(null); Map<Integer, ItemStack> exceeded = playerInv.addItem(onCursor); for (ItemStack stack : exceeded.values()) { if (stack.getType() == Material.AIR) { continue; } player.getWorld().dropItem(player.getLocation(), stack); } player.updateInventory(); player.setMaxHealth(maxHealth); player.setHealth(health); player.setFoodLevel(foodLevel); player.setLevel(level); player.setExp(experience); player.setAllowFlight(allowFlight); player.setFlying(isFlying); /* Remove current potion effects */ Collection<PotionEffect> effects = player.getActivePotionEffects(); for (PotionEffect effect : effects) { player.removePotionEffect(effect.getType()); } player.addPotionEffects(activeEffects); player.setExhaustion(exhaustion); player.setSaturation(saturation); player.setFallDistance(fallDistance); player.setFireTicks(fireTicks); if (scoreboard != player.getScoreboard()) { Scoreboard showBoard = scoreboard; if (scoreboard == null) { showBoard = Bukkit.getScoreboardManager().getMainScoreboard(); } player.setScoreboard(showBoard); } if (teleport) { player.teleport(location); } Location compassTarget = this.compassTarget; if (compassTarget == null) { compassTarget = player.getWorld().getSpawnLocation(); } player.setCompassTarget(compassTarget); for (WeakReference<Player> ref : cantSee) { Player cantSeePlayer = ref.get(); if (cantSeePlayer == null) { // Player object has been garbage-collected continue; } if (!cantSeePlayer.isOnline()) { continue; } player.hidePlayer(cantSeePlayer); } player.setGameMode(gamemode); } public void updateState(Player player, boolean location, GameMode mode) { PlayerInventory inventory = player.getInventory(); boolean is1_9 = MinecraftVersion.getImplementationVersion().compareTo(MinecraftVersion.V1_9) >= 0; boolean isSimpleSize = inventory.getContents().length <= SIMPLE_INVENTORY_SIZE; ItemStack[] contents = inventory.getContents(); ItemStack[] inventoryArray = new ItemStack[is1_9 && !isSimpleSize ? inventory.getSize() : INVENTORY_SIZE]; if (!is1_9 || isSimpleSize) { ItemStack[] armor = inventory.getArmorContents(); System.arraycopy(contents, 0, inventoryArray, 0, contents.length); System.arraycopy(armor, 0, inventoryArray, inventoryArray.length - ARMOR_INVENTORY_SIZE, armor.length); } else { System.arraycopy(contents, 0, inventoryArray, 0, contents.length); } /* Initialize the state with the current player state */ setInventory(inventoryArray); setOnCursor(player.getItemOnCursor()); setGamemode(mode != null ? mode : player.getGameMode()); setMaxHealth(player.getMaxHealth()); setHealth(player.getHealth(), player.getMaxHealth()); setFoodLevel(player.getFoodLevel()); setLevel(player.getLevel()); setExperience(player.getExp()); setAllowFlight(player.getAllowFlight()); setFlying(player.isFlying()); setActiveEffects(player.getActivePotionEffects()); setExhaustion(player.getExhaustion()); setSaturation(player.getSaturation()); setFallDistance(player.getFallDistance()); setFireTicks(player.getFireTicks()); if (location) { setLocation(player.getLocation()); } setScoreboard(player.getScoreboard()); setCompassTarget(player.getCompassTarget()); List<WeakReference<Player>> cantSee = Lists.newArrayList(); for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { if (player.canSee(onlinePlayer)) { continue; } WeakReference<Player> ref = new WeakReference<Player>(onlinePlayer); cantSee.add(ref); } setCantSee(cantSee); } public ItemStack[] getInventory() { return inventory; } public void setInventory(ItemStack[] inventory) { this.inventory = inventory; } public ItemStack getOnCursor() { return onCursor; } public void setOnCursor(ItemStack onCursor) { this.onCursor = onCursor; } public GameMode getGamemode() { return gamemode; } public void setGamemode(GameMode gamemode) { this.gamemode = gamemode; } public double getMaxHealth() { return maxHealth; } public void setMaxHealth(double maxHealth) { this.maxHealth = maxHealth; } public double getHealth() { return health; } public void setHealth(double health, double maxHealth) { if (health <= maxHealth) { this.health = health; } else { this.health = maxHealth; } } public int getFoodLevel() { return foodLevel; } public void setFoodLevel(int foodLevel) { this.foodLevel = foodLevel; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public float getExperience() { return experience; } public void setExperience(float experience) { this.experience = experience; } public boolean isAllowFlight() { return allowFlight; } public void setAllowFlight(boolean allowFlight) { this.allowFlight = allowFlight; } public boolean isFlying() { return isFlying; } public void setFlying(boolean isFlying) { this.isFlying = isFlying; } public Collection<PotionEffect> getActiveEffects() { return activeEffects; } public void setActiveEffects(Collection<PotionEffect> activeEffects) { this.activeEffects = activeEffects; } public float getExhaustion() { return exhaustion; } public void setExhaustion(float exhaustion) { this.exhaustion = exhaustion; } public float getSaturation() { return saturation; } public void setSaturation(float saturation) { this.saturation = saturation; } public float getFallDistance() { return fallDistance; } public void setFallDistance(float fallDistance) { this.fallDistance = fallDistance; } public int getFireTicks() { return fireTicks; } public void setFireTicks(int fireTicks) { this.fireTicks = fireTicks; } public List<WeakReference<Player>> getCantSee() { return cantSee; } public void setCantSee(List<WeakReference<Player>> cantSee) { this.cantSee = cantSee; } public Scoreboard getScoreboard() { return scoreboard; } public void setScoreboard(Scoreboard scoreboard) { this.scoreboard = scoreboard; } public Location getCompassTarget() { return compassTarget; } public void setCompassTarget(Location compassTarget) { this.compassTarget = compassTarget; } public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((activeEffects == null) ? 0 : activeEffects.hashCode()); result = prime * result + (allowFlight ? 1231 : 1237); result = prime * result + ((cantSee == null) ? 0 : cantSee.hashCode()); result = prime * result + ((compassTarget == null) ? 0 : compassTarget.hashCode()); result = prime * result + Float.floatToIntBits(exhaustion); result = prime * result + Float.floatToIntBits(experience); result = prime * result + Float.floatToIntBits(fallDistance); result = prime * result + fireTicks; result = prime * result + foodLevel; result = prime * result + ((gamemode == null) ? 0 : gamemode.hashCode()); long temp; temp = Double.doubleToLongBits(health); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + Arrays.hashCode(inventory); result = prime * result + (isFlying ? 1231 : 1237); result = prime * result + level; result = prime * result + ((location == null) ? 0 : location.hashCode()); result = prime * result + Float.floatToIntBits(saturation); result = prime * result + ((scoreboard == null) ? 0 : scoreboard.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PlayerStateHolder other = (PlayerStateHolder) obj; if (activeEffects == null) { if (other.activeEffects != null) return false; } else if (!activeEffects.equals(other.activeEffects)) return false; if (allowFlight != other.allowFlight) return false; if (cantSee == null) { if (other.cantSee != null) return false; } else if (!cantSee.equals(other.cantSee)) return false; if (compassTarget == null) { if (other.compassTarget != null) return false; } else if (!compassTarget.equals(other.compassTarget)) return false; if (Float.floatToIntBits(exhaustion) != Float.floatToIntBits(other.exhaustion)) return false; if (Float.floatToIntBits(experience) != Float.floatToIntBits(other.experience)) return false; if (Float.floatToIntBits(fallDistance) != Float.floatToIntBits(other.fallDistance)) return false; if (fireTicks != other.fireTicks) return false; if (foodLevel != other.foodLevel) return false; if (gamemode != other.gamemode) return false; if (Double.doubleToLongBits(health) != Double.doubleToLongBits(other.health)) return false; if (!Arrays.equals(inventory, other.inventory)) return false; if (isFlying != other.isFlying) return false; if (level != other.level) return false; if (location == null) { if (other.location != null) return false; } else if (!location.equals(other.location)) return false; if (Float.floatToIntBits(saturation) != Float.floatToIntBits(other.saturation)) return false; if (scoreboard == null) { if (other.scoreboard != null) return false; } else if (!scoreboard.equals(other.scoreboard)) return false; return true; } @Override public String toString() { return "PlayerStateHolder [inventory=" + Arrays.toString(inventory) + ", gamemode=" + gamemode + ", health=" + health + ", foodLevel=" + foodLevel + ", level=" + level + ", experience=" + experience + ", allowFlight=" + allowFlight + ", isFlying=" + isFlying + ", activeEffects=" + activeEffects + ", exhaustion=" + exhaustion + ", saturation=" + saturation + ", fallDistance=" + fallDistance + ", fireTicks=" + fireTicks + ", cantSee=" + cantSee + ", scoreboard=" + scoreboard + ", compassTarget=" + compassTarget + ", location=" + location + "]"; } }