diff --git a/src/main/java/com/garbagemule/MobArena/ArenaClass.java b/src/main/java/com/garbagemule/MobArena/ArenaClass.java index 7c5ab8b..068cc95 100644 --- a/src/main/java/com/garbagemule/MobArena/ArenaClass.java +++ b/src/main/java/com/garbagemule/MobArena/ArenaClass.java @@ -351,7 +351,7 @@ public class ArenaClass Arena arena = am.getArenaWithPlayer(p); if (arena != null) { try { - arena.getInventoryManager().restoreInv(p); + arena.getInventoryManager().equip(p); removeBannedItems(p.getInventory()); } catch (Exception e) { am.getPlugin().getLogger().severe("Failed to give " + p.getName() + " their own items: " + e.getMessage()); diff --git a/src/main/java/com/garbagemule/MobArena/ArenaImpl.java b/src/main/java/com/garbagemule/MobArena/ArenaImpl.java index dcdb94d..7fde240 100644 --- a/src/main/java/com/garbagemule/MobArena/ArenaImpl.java +++ b/src/main/java/com/garbagemule/MobArena/ArenaImpl.java @@ -4,6 +4,10 @@ import static com.garbagemule.MobArena.util.config.ConfigUtils.makeSection; import com.garbagemule.MobArena.ArenaClass.ArmorType; import com.garbagemule.MobArena.ScoreboardManager.NullScoreboardManager; +import com.garbagemule.MobArena.steps.Step; +import com.garbagemule.MobArena.steps.StepFactory; +import com.garbagemule.MobArena.steps.PlayerJoinArena; +import com.garbagemule.MobArena.steps.PlayerSpecArena; import com.garbagemule.MobArena.events.ArenaEndEvent; import com.garbagemule.MobArena.events.ArenaPlayerDeathEvent; import com.garbagemule.MobArena.events.ArenaPlayerJoinEvent; @@ -17,16 +21,8 @@ import com.garbagemule.MobArena.repairable.Repairable; import com.garbagemule.MobArena.repairable.RepairableComparator; import com.garbagemule.MobArena.repairable.RepairableContainer; import com.garbagemule.MobArena.things.Thing; -import com.garbagemule.MobArena.time.Time; -import com.garbagemule.MobArena.time.TimeStrategy; -import com.garbagemule.MobArena.time.TimeStrategyLocked; -import com.garbagemule.MobArena.time.TimeStrategyNull; import com.garbagemule.MobArena.util.ClassChests; -import com.garbagemule.MobArena.util.Delays; -import com.garbagemule.MobArena.util.Enums; -import com.garbagemule.MobArena.util.ItemParser; import com.garbagemule.MobArena.util.inventory.InventoryManager; -import com.garbagemule.MobArena.util.inventory.InventoryUtils; import com.garbagemule.MobArena.util.timer.AutoStartTimer; import com.garbagemule.MobArena.util.timer.StartDelayTimer; import com.garbagemule.MobArena.waves.SheepBouncer; @@ -34,7 +30,6 @@ import com.garbagemule.MobArena.waves.WaveManager; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Chunk; -import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -65,6 +60,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.PriorityBlockingQueue; +import java.util.logging.Level; import java.util.stream.Collectors; public class ArenaImpl implements Arena @@ -94,7 +90,6 @@ public class ArenaImpl implements Arena private RewardManager rewardManager; private ClassLimitManager limitManager; private Map arenaPlayerMap; - private Map playerData = new HashMap<>(); private Set arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, deadPlayers; private Set movingPlayers; @@ -123,7 +118,6 @@ public class ArenaImpl implements Arena // Misc private ArenaListener eventListener; private List entryFee; - private TimeStrategy timeStrategy; private AutoStartTimer autoStartTimer; private StartDelayTimer startDelayTimer; private boolean isolatedChat; @@ -134,6 +128,11 @@ public class ArenaImpl implements Arena // Last player standing private Player lastStanding; + // Actions + private Map histories; + private StepFactory playerJoinArena; + private StepFactory playerSpecArena; + /** * Primary constructor. Requires a name and a world. */ @@ -152,7 +151,7 @@ public class ArenaImpl implements Arena this.running = false; this.edit = false; - this.inventoryManager = new InventoryManager(this); + this.inventoryManager = new InventoryManager(); this.rewardManager = new RewardManager(this); // Warps, points and locations @@ -220,16 +219,17 @@ public class ArenaImpl implements Arena this.isolatedChat = settings.getBoolean("isolated-chat", false); - String timeString = settings.getString("player-time-in-arena", "world"); - Time time = Enums.getEnumFromString(Time.class, timeString); - this.timeStrategy = (time != null ? new TimeStrategyLocked(time) : new TimeStrategyNull()); - // Scoreboards this.scoreboard = (settings.getBoolean("use-scoreboards", true) ? new ScoreboardManager(this) : new NullScoreboardManager(this)); // Messenger String prefix = settings.getString("prefix", ""); this.messenger = !prefix.isEmpty() ? new Messenger(prefix) : plugin.getGlobalMessenger(); + + // Actions + this.histories = new HashMap<>(); + this.playerJoinArena = PlayerJoinArena.create(this); + this.playerSpecArena = PlayerSpecArena.create(this); } @@ -354,12 +354,6 @@ public class ArenaImpl implements Arena return waveManager; } - @Override - public Location getPlayerEntry(Player p) { - PlayerData mp = playerData.get(p); - return (mp != null ? mp.entry() : null); - } - @Override public ArenaListener getEventListener() { return eventListener; @@ -523,15 +517,7 @@ public class ArenaImpl implements Arena movingPlayers.add(p); p.teleport(region.getArenaWarp()); movingPlayers.remove(p); - p.setAllowFlight(false); - p.setFlying(false); - //movePlayerToLocation(p, region.getArenaWarp()); - setHealth(p, p.getMaxHealth()); - p.setFoodLevel(20); - if (settings.getBoolean("display-waves-as-level", false)) { - p.setLevel(0); - p.setExp(0.0f); - } + assignClassPermissions(p); arenaPlayerMap.get(p).resetStats(); @@ -678,6 +664,10 @@ public class ArenaImpl implements Arena } movingPlayers.add(p); + rollback(p); + + specPlayers.remove(p); + // Announce globally (must happen before moving player) if (settings.getBoolean("global-join-announce", false)) { if (lobbyPlayers.isEmpty()) { @@ -687,18 +677,17 @@ public class ArenaImpl implements Arena } } - MAUtils.sitPets(p); - movePlayerToLobby(p); - takeFee(p); - storePlayerData(p, loc); - removePotionEffects(p); - setHealth(p, p.getMaxHealth()); - p.setFoodLevel(20); - if (settings.getBoolean("display-timer-as-level", false)) { - p.setLevel(0); - p.setExp(0.0f); + Step step = playerJoinArena.create(p); + try { + step.run(); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, () -> "Player " + p.getName() + " couldn't join arena " + name); + return false; } - p.setGameMode(GameMode.SURVIVAL); + histories.put(p, step); + + lobbyPlayers.add(p); + plugin.getArenaMaster().addPlayer(p, this); arenaPlayerMap.put(p, new ArenaPlayer(p, this, plugin)); @@ -775,7 +764,6 @@ public class ArenaImpl implements Arena removeClassPermissions(p); removePotionEffects(p); - restoreInvAndExp(p); if (inLobby(p) || inArena(p)) { refund(p); } @@ -792,7 +780,6 @@ public class ArenaImpl implements Arena } } - movePlayerToEntry(p); discardPlayer(p); endArena(); @@ -834,8 +821,9 @@ public class ArenaImpl implements Arena return; } - setHealth(p, p.getMaxHealth()); - Delays.revivePlayer(plugin, this, p); + p.setHealth(20.0); + plugin.getServer().getScheduler() + .scheduleSyncDelayedTask(plugin, () -> revivePlayer(p)); endArena(); } @@ -864,37 +852,25 @@ public class ArenaImpl implements Arena } deadPlayers.remove(p); - revivePlayer(p); + plugin.getServer().getScheduler() + .scheduleSyncDelayedTask(plugin, () -> revivePlayer(p)); } @Override @SuppressWarnings("deprecation") public void revivePlayer(Player p) { - Delays.douse(plugin, p, 1); removeClassPermissions(p); removePotionEffects(p); + discardPlayer(p); if (settings.getBoolean("spectate-on-death", true)) { - movePlayerToSpec(p); - messenger.tell(p, Msg.SPEC_FROM_ARENA); - messenger.tell(p, Msg.MISC_MA_LEAVE_REMINDER); - } else { - restoreInvAndExp(p); - movePlayerToEntry(p); - discardPlayer(p); + playerSpec(p, null); } - p.updateInventory(); } @Override public Location getRespawnLocation(Player p) { - Location l = null; - if (settings.getBoolean("spectate-on-death", true)) { - l = region.getSpecWarp(); - } else { - l = playerData.get(p).entry(); - } - return l; + return region.getSpecWarp(); } @Override @@ -904,14 +880,37 @@ public class ArenaImpl implements Arena } movingPlayers.add(p); - storePlayerData(p, loc); - MAUtils.sitPets(p); - movePlayerToSpec(p); + + rollback(p); + + Step step = playerSpecArena.create(p); + try { + step.run(); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, () -> "Player " + p.getName() + " couldn't spec arena " + name); + return; + } + histories.put(p, step); + + specPlayers.add(p); + plugin.getArenaMaster().addPlayer(p, this); messenger.tell(p, Msg.SPEC_PLAYER_SPECTATE); movingPlayers.remove(p); } + private void rollback(Player p) { + Step step = histories.remove(p); + if (step == null) { + return; + } + try { + step.undo(); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, () -> "Failed to revert player " + p.getName()); + } + } + private void spawnPets() { for (Player p : arenaPlayers) { // Skip players who are either null or offline @@ -1078,33 +1077,6 @@ public class ArenaImpl implements Arena scheduleTask(sheepBouncer, settings.getInt("first-wave-delay", 5) * 20); } - @Override - public void storePlayerData(Player p, Location loc) - { - plugin.getArenaMaster().addPlayer(p, this); - - PlayerData mp = playerData.get(p); - - // If there's no player stored, create a new one! - if (mp == null) { - if (region.getExitWarp() != null) loc = region.getExitWarp(); - mp = new PlayerData(p, loc); - playerData.put(p, mp); - } - - // At any rate, update the data. - mp.update(); - - // And update the inventory as well. - try { - inventoryManager.storeInv(p); - inventoryManager.clearInventory(p); - } catch (Exception e) { - e.printStackTrace(); - plugin.getLogger().severe("Failed to store inventory for player " + p.getName() + "!"); - } - } - @Override public void storeContainerContents() { @@ -1124,80 +1096,16 @@ public class ArenaImpl implements Arena } } - @Override - public void movePlayerToLobby(Player p) - { - specPlayers.remove(p); // If joining from spec area - lobbyPlayers.add(p); - p.teleport(region.getLobbyWarp()); - p.setAllowFlight(false); - p.setFlying(false); - timeStrategy.setPlayerTime(p); - } - - @Override - public void movePlayerToSpec(Player p) - { - specPlayers.add(p); - p.teleport(region.getSpecWarp()); - timeStrategy.setPlayerTime(p); - } - - @Override - public void movePlayerToEntry(Player p) - { - Location entry = playerData.get(p).entry(); - if (entry == null || p.isDead()) return; - - p.teleport(entry); - timeStrategy.resetPlayerTime(p); - - p.setGameMode(playerData.get(p).getMode()); - p.addPotionEffects(playerData.get(p).getPotionEffects()); - } - - private void restoreInvAndExp(Player p) { - inventoryManager.clearInventory(p); - try { - inventoryManager.restoreInv(p); - inventoryManager.clearCache(p); - } catch (Exception e) { - e.printStackTrace(); - plugin.getLogger().severe("Failed to restore inventory for player " + p.getName() + "!"); - } - rewardManager.grantRewards(p); - - // Try to prevent XP issues - if (lobbyPlayers.contains(p) - || !settings.getBoolean("keep-exp") - || settings.getBoolean("display-waves-as-level", false) - || settings.getBoolean("display-timer-as-level", false)) { - playerData.get(p).restoreData(); - } - else { - p.setFoodLevel(playerData.get(p).food()); - } - } - @Override public void discardPlayer(Player p) { + rollback(p); plugin.getArenaMaster().removePlayer(p); clearPlayer(p); } private void clearPlayer(Player p) { - // Remove the player data completely. - PlayerData mp = playerData.remove(p); - - // Health must be handled in a certain way because of Heroes - // Math.min to guard for ItemLoreStats weirdness - setHealth(p, Math.min(p.getMaxHealth(), mp.health())); - - // Put out fire. - Delays.douse(plugin, p, 3); - // Remove pets. monsterManager.removePets(p); @@ -1211,10 +1119,6 @@ public class ArenaImpl implements Arena scoreboard.removePlayer(p); } - private void setHealth(Player p, double health) { - p.setHealth(health); - } - @Override public void repairBlocks() { @@ -1245,7 +1149,7 @@ public class ArenaImpl implements Arena return; } - inventoryManager.clearInventory(p); + InventoryManager.clearInventory(p); arenaPlayer.setArenaClass(arenaClass); arenaClass.grantItems(p); @@ -1265,7 +1169,7 @@ public class ArenaImpl implements Arena return; } - inventoryManager.clearInventory(p); + InventoryManager.clearInventory(p); arenaPlayer.setArenaClass(arenaClass); PlayerInventory inv = p.getInventory(); diff --git a/src/main/java/com/garbagemule/MobArena/MobArena.java b/src/main/java/com/garbagemule/MobArena/MobArena.java index e5cdff4..67ed86b 100644 --- a/src/main/java/com/garbagemule/MobArena/MobArena.java +++ b/src/main/java/com/garbagemule/MobArena/MobArena.java @@ -8,14 +8,12 @@ import com.garbagemule.MobArena.listeners.MagicSpellsListener; import com.garbagemule.MobArena.things.ThingManager; import com.garbagemule.MobArena.util.VersionChecker; import com.garbagemule.MobArena.util.config.ConfigUtils; -import com.garbagemule.MobArena.util.inventory.InventoryManager; import com.garbagemule.MobArena.waves.ability.AbilityManager; import net.milkbowl.vault.economy.Economy; import org.bukkit.ChatColor; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; @@ -26,10 +24,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashSet; import java.util.List; import java.util.Random; -import java.util.Set; /** * MobArena @@ -40,9 +36,6 @@ public class MobArena extends JavaPlugin private ArenaMaster arenaMaster; private CommandHandler commandHandler; - // Inventories from disconnects - private Set inventoriesToRestore; - // Vault private Economy economy; @@ -91,9 +84,6 @@ public class MobArena extends JavaPlugin arenaMaster = new ArenaMasterImpl(this); arenaMaster.initialize(); - // Register any inventories to restore. - registerInventories(); - // Register event listeners registerListeners(); @@ -258,32 +248,6 @@ public class MobArena extends JavaPlugin "Note: You -must- use spaces instead of tabs!"; } - private void registerInventories() { - this.inventoriesToRestore = new HashSet<>(); - - File dir = new File(getDataFolder(), "inventories"); - if (!dir.exists()) { - dir.mkdir(); - return; - } - - for (File f : dir.listFiles()) { - if (f.getName().endsWith(".inv")) { - inventoriesToRestore.add(f.getName().substring(0, f.getName().indexOf("."))); - } - } - } - - public void restoreInventory(Player p) { - if (!inventoriesToRestore.contains(p.getName())) { - return; - } - - if (InventoryManager.restoreFromFile(this, p)) { - inventoriesToRestore.remove(p.getName()); - } - } - public Economy getEconomy() { return economy; } diff --git a/src/main/java/com/garbagemule/MobArena/PlayerData.java b/src/main/java/com/garbagemule/MobArena/PlayerData.java deleted file mode 100644 index b8fafcf..0000000 --- a/src/main/java/com/garbagemule/MobArena/PlayerData.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.garbagemule.MobArena; - -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; - -import java.util.Collection; - -public class PlayerData -{ - private Player player; - - private double health; - private int food, level; - private float exp; - private GameMode mode = null; - private Location entry = null; - private Collection potions; - - public PlayerData(Player player, Location loc) { - this.player = player; - this.mode = player.getGameMode(); - this.potions = player.getActivePotionEffects(); - this.entry = loc; - - update(); - } - - /** - * Updates the information that is restored, when a player - * dies in the arena, that is, health, food level, and - * experience. Used when a player re-joins an arena while - * already being a spectator. - */ - public void update() { - this.health = player.getHealth(); - this.food = player.getFoodLevel(); - this.level = player.getLevel(); - this.exp = player.getExp(); - } - - /** - * Restores health, food level, and experience as per the - * currently stored values of this object. Used when a - * player leaves the arena. - */ - public void restoreData() { - player.setFoodLevel(food); - player.setLevel(level); - player.setExp(exp); - } - - public Player getPlayer() { - return player; - } - - public double health() { - return health; - } - - public void setHealth(int health) { - this.health = health; - } - - public int food() { - return food; - } - - public void setFood(int food) { - this.food = food; - } - - public int level() { - return level; - } - - public void setLevel(int level) { - this.level = level; - } - - public float exp() { - return exp; - } - - public void setExp(int exp) { - this.exp = exp; - } - - public GameMode getMode() { - return mode; - } - - public Collection getPotionEffects() { - return potions; - } - - public void setMode(GameMode mode) { - this.mode = mode; - } - - public Location entry() { - return entry; - } - - public void setEntry(Location entry) { - this.entry = entry; - } -} diff --git a/src/main/java/com/garbagemule/MobArena/commands/admin/RestoreCommand.java b/src/main/java/com/garbagemule/MobArena/commands/admin/RestoreCommand.java index c5d245c..7af13cd 100644 --- a/src/main/java/com/garbagemule/MobArena/commands/admin/RestoreCommand.java +++ b/src/main/java/com/garbagemule/MobArena/commands/admin/RestoreCommand.java @@ -5,6 +5,7 @@ import com.garbagemule.MobArena.commands.CommandInfo; import com.garbagemule.MobArena.framework.ArenaMaster; import com.garbagemule.MobArena.util.inventory.InventoryManager; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; @CommandInfo( name = "restore", @@ -20,12 +21,17 @@ public class RestoreCommand implements Command // Require a player name if (args.length != 1) return false; - if (am.getArenaWithPlayer(args[0]) != null) { + Player player = am.getPlugin().getServer().getPlayer(args[0]); + if (player == null) { + am.getGlobalMessenger().tell(sender, "Player not found."); + return true; + } + if (am.getArenaWithPlayer(player) != null) { am.getGlobalMessenger().tell(sender, "Player is currently in an arena."); return true; } - if (InventoryManager.restoreFromFile(am.getPlugin(), am.getPlugin().getServer().getPlayer(args[0]))) { + if (InventoryManager.restoreFromFile(am.getPlugin(), player)) { am.getGlobalMessenger().tell(sender, "Restored " + args[0] + "'s inventory!"); } else { am.getGlobalMessenger().tell(sender, "Failed to restore " + args[0] + "'s inventory."); diff --git a/src/main/java/com/garbagemule/MobArena/framework/Arena.java b/src/main/java/com/garbagemule/MobArena/framework/Arena.java index 0a9d67f..41f1016 100644 --- a/src/main/java/com/garbagemule/MobArena/framework/Arena.java +++ b/src/main/java/com/garbagemule/MobArena/framework/Arena.java @@ -81,8 +81,6 @@ public interface Arena WaveManager getWaveManager(); - Location getPlayerEntry(Player p); - ArenaListener getEventListener(); void setLeaderboard(Leaderboard leaderboard); @@ -168,18 +166,10 @@ public interface Arena void playerSpec(Player p, Location loc); - void storePlayerData(Player p, Location loc); - void storeContainerContents(); void restoreContainerContents(); - void movePlayerToLobby(Player p); - - void movePlayerToSpec(Player p); - - void movePlayerToEntry(Player p); - void discardPlayer(Player p); void repairBlocks(); diff --git a/src/main/java/com/garbagemule/MobArena/listeners/MAGlobalListener.java b/src/main/java/com/garbagemule/MobArena/listeners/MAGlobalListener.java index 17deb04..7abe42a 100644 --- a/src/main/java/com/garbagemule/MobArena/listeners/MAGlobalListener.java +++ b/src/main/java/com/garbagemule/MobArena/listeners/MAGlobalListener.java @@ -322,8 +322,6 @@ public class MAGlobalListener implements Listener return; } } - - plugin.restoreInventory(event.getPlayer()); } public enum TeleportResponse { diff --git a/src/main/java/com/garbagemule/MobArena/steps/ClearInventory.java b/src/main/java/com/garbagemule/MobArena/steps/ClearInventory.java new file mode 100644 index 0000000..8f56058 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/ClearInventory.java @@ -0,0 +1,66 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.logging.Level; + +class ClearInventory extends PlayerStep { + private final File inventories; + private final Arena arena; + + private ItemStack[] contents; + private File backup; + + private ClearInventory(Player player, Arena arena) { + super(player); + this.inventories = new File(arena.getPlugin().getDataFolder(), "inventories"); + this.arena = arena; + } + + @Override + public void run() { + contents = player.getInventory().getContents(); + createBackup(); + + player.getInventory().clear(); + } + + @Override + public void undo() { + player.getInventory().setContents(contents); + + arena.getInventoryManager().remove(player); + deleteBackup(); + } + + private void createBackup() { + YamlConfiguration yaml = new YamlConfiguration(); + yaml.set("contents", contents); + + backup = new File(inventories, player.getUniqueId().toString()); + try { + yaml.save(backup); + } catch (IOException e) { + throw new RuntimeException("Failed to store inventory for " + player.getName(), e); + } + arena.getInventoryManager().put(player, contents); + } + + private void deleteBackup() { + try { + Files.delete(backup.toPath()); + } catch (IOException e) { + arena.getPlugin().getLogger().log(Level.WARNING, "Couldn't delete backup inventory file for " + player.getName(), e); + } + } + + static StepFactory create(Arena arena) { + return player -> new ClearInventory(player, arena); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/ClearPotionEffects.java b/src/main/java/com/garbagemule/MobArena/steps/ClearPotionEffects.java new file mode 100644 index 0000000..2ed9b56 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/ClearPotionEffects.java @@ -0,0 +1,34 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; + +import java.util.Collection; +import java.util.Collections; + +class ClearPotionEffects extends PlayerStep { + private Collection effects; + + private ClearPotionEffects(Player player) { + super(player); + effects = Collections.emptyList(); + } + + @Override + public void run() { + effects = player.getActivePotionEffects(); + + effects.stream() + .map(PotionEffect::getType) + .forEach(player::removePotionEffect); + } + + @Override + public void undo() { + player.addPotionEffects(effects); + } + + static StepFactory create() { + return ClearPotionEffects::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/Defer.java b/src/main/java/com/garbagemule/MobArena/steps/Defer.java new file mode 100644 index 0000000..031a37e --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/Defer.java @@ -0,0 +1,36 @@ +package com.garbagemule.MobArena.steps; + +/** + * This wrapper allows a delegate {@link Step}'s {@link Step#run() run()} + * operation to be deferred until this wrapper's {@link Step#undo() undo()} + * operation is invoked. This is useful when a Step should only be invoked + * during rollback, but not during the initial procedure. + * + * @see GrantRewards + */ +class Defer implements Step { + private Step step; + + private Defer(Step step) { + this.step = step; + } + + @Override + public void run() { + // OK BOSS + } + + @Override + public void undo() { + step.run(); + } + + @Override + public String toString() { + return "deferred(" + step + ")"; + } + + static StepFactory it(StepFactory factory) { + return player -> new Defer(factory.create(player)); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/GrantRewards.java b/src/main/java/com/garbagemule/MobArena/steps/GrantRewards.java new file mode 100644 index 0000000..8275a46 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/GrantRewards.java @@ -0,0 +1,27 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.entity.Player; + +class GrantRewards extends PlayerStep { + private final Arena arena; + + private GrantRewards(Player player, Arena arena) { + super(player); + this.arena = arena; + } + + @Override + public void run() { + arena.getRewardManager().grantRewards(player); + } + + @Override + public void undo() { + // OK BOSS + } + + static StepFactory create(Arena arena) { + return player -> new GrantRewards(player, arena); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/MovePlayerStep.java b/src/main/java/com/garbagemule/MobArena/steps/MovePlayerStep.java new file mode 100644 index 0000000..5a82aea --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/MovePlayerStep.java @@ -0,0 +1,29 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.function.Supplier; + +abstract class MovePlayerStep extends PlayerStep { + private final Supplier destination; + + private Location location; + + MovePlayerStep(Player player, Supplier destination) { + super(player); + this.destination = destination; + } + + @Override + public void run() { + location = player.getLocation(); + + player.teleport(destination.get()); + } + + @Override + public void undo() { + player.teleport(location); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/MoveToExit.java b/src/main/java/com/garbagemule/MobArena/steps/MoveToExit.java new file mode 100644 index 0000000..6e8add3 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/MoveToExit.java @@ -0,0 +1,14 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.entity.Player; + +class MoveToExit extends MovePlayerStep { + private MoveToExit(Player player, Arena arena) { + super(player, () -> arena.getRegion().getExitWarp()); + } + + static StepFactory create(Arena arena) { + return player -> new MoveToExit(player, arena); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/MoveToLobby.java b/src/main/java/com/garbagemule/MobArena/steps/MoveToLobby.java new file mode 100644 index 0000000..11cea8c --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/MoveToLobby.java @@ -0,0 +1,14 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.entity.Player; + +class MoveToLobby extends MovePlayerStep { + private MoveToLobby(Player player, Arena arena) { + super(player, () -> arena.getRegion().getLobbyWarp()); + } + + static StepFactory create(Arena arena) { + return player -> new MoveToLobby(player, arena); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/MoveToSpec.java b/src/main/java/com/garbagemule/MobArena/steps/MoveToSpec.java new file mode 100644 index 0000000..7089686 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/MoveToSpec.java @@ -0,0 +1,14 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.entity.Player; + +class MoveToSpec extends MovePlayerStep { + private MoveToSpec(Player player, Arena arena) { + super(player, () -> arena.getRegion().getSpecWarp()); + } + + static StepFactory create(Arena arena) { + return player -> new MoveToSpec(player, arena); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/PlayerJoinArena.java b/src/main/java/com/garbagemule/MobArena/steps/PlayerJoinArena.java new file mode 100644 index 0000000..3dceadc --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/PlayerJoinArena.java @@ -0,0 +1,32 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerJoinArena { + public static StepFactory create(Arena arena) { + ConfigurationSection settings = arena.getSettings(); + + List factories = new ArrayList<>(); + + if (arena.getRegion().getExitWarp() != null) { + factories.add(Defer.it(MoveToExit.create(arena))); + } + factories.add(SitPets.create()); + factories.add(MoveToLobby.create(arena)); + factories.add(SetGameMode.create()); + factories.add(Defer.it(GrantRewards.create(arena))); + factories.add(ClearInventory.create(arena)); + factories.add(ClearPotionEffects.create()); + factories.add(SetFlying.create()); + factories.add(SetHealth.create()); + factories.add(SetHunger.create()); + factories.add(SetExperience.create()); + factories.add(SetPlayerTime.create(settings)); + + return PlayerMultiStep.create(factories, arena.getPlugin().getLogger()); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/PlayerMultiStep.java b/src/main/java/com/garbagemule/MobArena/steps/PlayerMultiStep.java new file mode 100644 index 0000000..67253f4 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/PlayerMultiStep.java @@ -0,0 +1,70 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * PlayerMultiSteps group lists of {@link StepFactory StepFactories} into a + * single unit, effectively realizing the Composite Pattern. + *

+ * Each factory is invoked to create a given {@link Step}, which is executed + * right away. If the Step succeeds, it is pushed to a history stack. The undo + * operation pops the successful Steps off the history stack one by one and + * runs the undo operation on each one in reverse order. + *

+ * If one Step fails, any previous successful Steps are rolled back by running + * their undo operations. + */ +class PlayerMultiStep extends PlayerStep { + private final List factories; + private final Logger logger; + + private Deque history; + + private PlayerMultiStep(Player player, List factories, Logger logger) { + super(player); + this.factories = factories; + this.logger = logger; + this.history = new ArrayDeque<>(); + } + + @Override + public void run() { + history.clear(); + + factories.forEach(factory -> { + Step step = factory.create(player); + try { + step.run(); + history.push(step); + logger.info("Step " + step + " OK"); + } catch (RuntimeException up) { + logger.log(Level.SEVERE, up, () -> "Failed to run step " + step); + undo(); + throw up; + } + }); + } + + @Override + public void undo() { + while (!history.isEmpty()) { + Step step = history.pop(); + try { + step.undo(); + logger.info("Rollback " + step + " OK"); + } catch (RuntimeException e) { + logger.log(Level.SEVERE, e, () -> "Failed to undo step " + step); + } + } + } + + static StepFactory create(List factories, Logger logger) { + return player -> new PlayerMultiStep(player, factories, logger); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/PlayerSpecArena.java b/src/main/java/com/garbagemule/MobArena/steps/PlayerSpecArena.java new file mode 100644 index 0000000..f8f34a4 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/PlayerSpecArena.java @@ -0,0 +1,31 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerSpecArena { + public static StepFactory create(Arena arena) { + ConfigurationSection settings = arena.getSettings(); + + List factories = new ArrayList<>(); + + if (arena.getRegion().getExitWarp() != null) { + factories.add(Defer.it(MoveToExit.create(arena))); + } + factories.add(SitPets.create()); + factories.add(MoveToSpec.create(arena)); + factories.add(SetGameMode.create()); + factories.add(ClearInventory.create(arena)); + factories.add(ClearPotionEffects.create()); + factories.add(SetFlying.create()); + factories.add(SetHealth.create()); + factories.add(SetHunger.create()); + factories.add(SetExperience.create()); + factories.add(SetPlayerTime.create(settings)); + + return PlayerMultiStep.create(factories, arena.getPlugin().getLogger()); + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/PlayerStep.java b/src/main/java/com/garbagemule/MobArena/steps/PlayerStep.java new file mode 100644 index 0000000..e7ee2b3 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/PlayerStep.java @@ -0,0 +1,18 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +abstract class PlayerStep implements Step { + protected final Player player; + + protected PlayerStep(Player player) { + this.player = player; + } + + @Override + public String toString() { + String step = getClass().getSimpleName(); + String target = player.getName(); + return step + "(" + target + ")"; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetExperience.java b/src/main/java/com/garbagemule/MobArena/steps/SetExperience.java new file mode 100644 index 0000000..6649afb --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetExperience.java @@ -0,0 +1,31 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +class SetExperience extends PlayerStep { + private int level; + private float exp; + + private SetExperience(Player player) { + super(player); + } + + @Override + public void run() { + level = player.getLevel(); + exp = player.getExp(); + + player.setExp(0); + player.setLevel(0); + } + + @Override + public void undo() { + player.setLevel(level); + player.setExp(exp); + } + + static StepFactory create() { + return SetExperience::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetFlying.java b/src/main/java/com/garbagemule/MobArena/steps/SetFlying.java new file mode 100644 index 0000000..d0d0963 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetFlying.java @@ -0,0 +1,35 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +class SetFlying extends PlayerStep { + private boolean allow; + private boolean flying; + private float speed; + + private SetFlying(Player player) { + super(player); + } + + @Override + public void run() { + allow = player.getAllowFlight(); + flying = player.isFlying(); + speed = player.getFlySpeed(); + + player.setFlySpeed(0); + player.setFlying(false); + player.setAllowFlight(false); + } + + @Override + public void undo() { + player.setAllowFlight(allow); + player.setFlying(flying); + player.setFlySpeed(speed); + } + + static StepFactory create() { + return SetFlying::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetGameMode.java b/src/main/java/com/garbagemule/MobArena/steps/SetGameMode.java new file mode 100644 index 0000000..f8e0abd --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetGameMode.java @@ -0,0 +1,28 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +class SetGameMode extends PlayerStep { + private GameMode mode; + + private SetGameMode(Player player) { + super(player); + } + + @Override + public void run() { + mode = player.getGameMode(); + + player.setGameMode(GameMode.SURVIVAL); + } + + @Override + public void undo() { + player.setGameMode(mode); + } + + static StepFactory create() { + return SetGameMode::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetHealth.java b/src/main/java/com/garbagemule/MobArena/steps/SetHealth.java new file mode 100644 index 0000000..1a1ecce --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetHealth.java @@ -0,0 +1,39 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +class SetHealth extends PlayerStep { + private static final double FULL_HEALTH = 20.0; + private static final int NORMAL_FIRE = -20; + private static final int NORMAL_AIR = 300; + + private double health; + private int fire; + private int air; + + private SetHealth(Player player) { + super(player); + } + + @Override + public void run() { + health = player.getHealth(); + fire = player.getFireTicks(); + air = player.getRemainingAir(); + + player.setRemainingAir(NORMAL_AIR); + player.setFireTicks(NORMAL_FIRE); + player.setHealth(FULL_HEALTH); + } + + @Override + public void undo() { + player.setHealth(health); + player.setFireTicks(fire); + player.setRemainingAir(air); + } + + static StepFactory create() { + return SetHealth::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetHunger.java b/src/main/java/com/garbagemule/MobArena/steps/SetHunger.java new file mode 100644 index 0000000..208a14c --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetHunger.java @@ -0,0 +1,39 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +class SetHunger extends PlayerStep { + private static final int FULL_FOOD = 20; + private static final float NORMAL_SATURATION = 5f; + private static final float NORMAL_EXHAUSTION = 0f; + + private int food; + private float saturation; + private float exhaustion; + + private SetHunger(Player player) { + super(player); + } + + @Override + public void run() { + food = player.getFoodLevel(); + saturation = player.getSaturation(); + exhaustion = player.getExhaustion(); + + player.setExhaustion(NORMAL_EXHAUSTION); + player.setSaturation(NORMAL_SATURATION); + player.setFoodLevel(FULL_FOOD); + } + + @Override + public void undo() { + player.setFoodLevel(food); + player.setSaturation(saturation); + player.setExhaustion(exhaustion); + } + + static StepFactory create() { + return SetHunger::new; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SetPlayerTime.java b/src/main/java/com/garbagemule/MobArena/steps/SetPlayerTime.java new file mode 100644 index 0000000..f0a0a3b --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SetPlayerTime.java @@ -0,0 +1,42 @@ +package com.garbagemule.MobArena.steps; + +import com.garbagemule.MobArena.time.Time; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +class SetPlayerTime extends PlayerStep { + private final Time time; + + private SetPlayerTime(Player player, Time time) { + super(player); + this.time = time; + } + + @Override + public void run() { + if (time != null) { + player.setPlayerTime(time.getTime(), false); + } + } + + @Override + public void undo() { + if (time != null) { + player.resetPlayerTime(); + } + } + + static StepFactory create(ConfigurationSection settings) { + Time time = parseTime(settings); + return player -> new SetPlayerTime(player, time); + } + + private static Time parseTime(ConfigurationSection settings) { + String value = settings.getString("player-time-in-arena", "world"); + try { + return Time.valueOf(value.toLowerCase()); + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/SitPets.java b/src/main/java/com/garbagemule/MobArena/steps/SitPets.java new file mode 100644 index 0000000..950f3d9 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/SitPets.java @@ -0,0 +1,79 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Ocelot; +import org.bukkit.entity.Player; +import org.bukkit.entity.Tameable; +import org.bukkit.entity.Wolf; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +class SitPets extends PlayerStep { + private List pets; + + private SitPets(Player player) { + super(player); + pets = Collections.emptyList(); + } + + @Override + public void run() { + pets = findNearbyPets(player); + + pets.forEach(SitPets.setSitting(true)); + } + + @Override + public void undo() { + pets.forEach(SitPets.setSitting(false)); + } + + static StepFactory create() { + return SitPets::new; + } + + private static List findNearbyPets(Player player) { + return player.getNearbyEntities(80, 40, 80).stream() + .filter(SitPets.isPetOwnedBy(player)) + .filter(SitPets::isFollowing) + .collect(Collectors.toList()); + } + + private static Predicate isPetOwnedBy(Player player) { + return entity -> { + switch (entity.getType()) { + case WOLF: + case OCELOT: + return player.equals(((Tameable) entity).getOwner()); + } + return false; + }; + } + + private static boolean isFollowing(Entity entity) { + switch (entity.getType()) { + case WOLF: + return !((Wolf) entity).isSitting(); + case OCELOT: + return !((Ocelot) entity).isSitting(); + } + return false; + } + + private static Consumer setSitting(boolean sitting) { + return entity -> { + switch (entity.getType()) { + case WOLF: + ((Wolf) entity).setSitting(sitting); + break; + case OCELOT: + ((Ocelot) entity).setSitting(sitting); + break; + } + }; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/Step.java b/src/main/java/com/garbagemule/MobArena/steps/Step.java new file mode 100644 index 0000000..1f9b7a8 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/Step.java @@ -0,0 +1,28 @@ +package com.garbagemule.MobArena.steps; + +/** + * Steps are small, self-contained pieces of behavior. + *

+ * The {@link #run()} method of a Step executes its behavior. This could be + * anything, but it usually involves a change in state, somewhere, + * and some Steps support the optional {@link #undo()} operation that attempts + * to revert the state back to what it was before the Step was executed. + *

+ * Steps realize the Command role of the Command Pattern. + * + * @see StepFactory + */ +public interface Step { + /** + * Execute the behavior of this Step. + */ + void run(); + + /** + * Undo a previous execution of this Step (optional operation). + *

+ * This method can be called without first invoking the {@link #run()} + * method. Doing so is silly. + */ + void undo(); +} diff --git a/src/main/java/com/garbagemule/MobArena/steps/StepFactory.java b/src/main/java/com/garbagemule/MobArena/steps/StepFactory.java new file mode 100644 index 0000000..2e6a38a --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/steps/StepFactory.java @@ -0,0 +1,11 @@ +package com.garbagemule.MobArena.steps; + +import org.bukkit.entity.Player; + +/** + * StepFactories create closures over {@link Player Players} and return + * {@link Step Steps} that operate on the given Player. + */ +public interface StepFactory { + Step create(Player player); +} diff --git a/src/main/java/com/garbagemule/MobArena/time/TimeStrategy.java b/src/main/java/com/garbagemule/MobArena/time/TimeStrategy.java deleted file mode 100644 index bba9295..0000000 --- a/src/main/java/com/garbagemule/MobArena/time/TimeStrategy.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.garbagemule.MobArena.time; - -import org.bukkit.entity.Player; - -public interface TimeStrategy -{ - /** - * Set the time enum used by setPlayerTime() - * @param time a Time enum - */ - void setTime(Time time); - - /** - * Set the local client time for the player. - * @param p a player - */ - void setPlayerTime(Player p); - - /** - * Reset the local client time for the player to the server time - * @param p a player - */ - void resetPlayerTime(Player p); -} diff --git a/src/main/java/com/garbagemule/MobArena/time/TimeStrategyLocked.java b/src/main/java/com/garbagemule/MobArena/time/TimeStrategyLocked.java deleted file mode 100644 index 3bdce23..0000000 --- a/src/main/java/com/garbagemule/MobArena/time/TimeStrategyLocked.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.garbagemule.MobArena.time; - -import org.bukkit.entity.Player; - -public class TimeStrategyLocked implements TimeStrategy -{ - private Time time; - - public TimeStrategyLocked(Time time) { - setTime(time); - } - - @Override - public void setTime(Time time) { - this.time = time; - } - - @Override - public void setPlayerTime(Player p) { - p.setPlayerTime(time.getTime(), false); - } - - @Override - public void resetPlayerTime(Player p) { - p.resetPlayerTime(); - } -} diff --git a/src/main/java/com/garbagemule/MobArena/time/TimeStrategyNull.java b/src/main/java/com/garbagemule/MobArena/time/TimeStrategyNull.java deleted file mode 100644 index 7fe0dc0..0000000 --- a/src/main/java/com/garbagemule/MobArena/time/TimeStrategyNull.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.garbagemule.MobArena.time; - -import org.bukkit.entity.Player; - -public class TimeStrategyNull implements TimeStrategy -{ - @Override - public void setTime(Time time) {} - - @Override - public void setPlayerTime(Player p) {} - - @Override - public void resetPlayerTime(Player p) {} -} diff --git a/src/main/java/com/garbagemule/MobArena/util/Delays.java b/src/main/java/com/garbagemule/MobArena/util/Delays.java deleted file mode 100644 index a86e9c3..0000000 --- a/src/main/java/com/garbagemule/MobArena/util/Delays.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.garbagemule.MobArena.util; - -import com.garbagemule.MobArena.MobArena; -import com.garbagemule.MobArena.framework.Arena; -import org.bukkit.entity.Player; - -public class Delays -{ - public static void douse(MobArena plugin, final Player p, long delay) { - if (!plugin.isEnabled()) return; - plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - public void run() { - if (p.isOnline()) { - p.setFireTicks(0); - } - } - }, delay); - } - - public static void revivePlayer(MobArena plugin, final Arena arena, final Player p) { - if (!plugin.isEnabled()) return; - plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - public void run() { - if (p.isOnline()) { - arena.revivePlayer(p); - } - } - }); - } -} diff --git a/src/main/java/com/garbagemule/MobArena/util/inventory/InventoryManager.java b/src/main/java/com/garbagemule/MobArena/util/inventory/InventoryManager.java index ecdc8d6..317ddbc 100644 --- a/src/main/java/com/garbagemule/MobArena/util/inventory/InventoryManager.java +++ b/src/main/java/com/garbagemule/MobArena/util/inventory/InventoryManager.java @@ -1,9 +1,7 @@ package com.garbagemule.MobArena.util.inventory; import com.garbagemule.MobArena.MobArena; -import com.garbagemule.MobArena.framework.Arena; import org.bukkit.Material; -import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -12,88 +10,39 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.logging.Level; public class InventoryManager { - private File dir; - private Map items, armor; + private Map inventories; - public InventoryManager(Arena arena) { - this.dir = new File(arena.getPlugin().getDataFolder(), "inventories"); - this.dir.mkdir(); - - this.items = new HashMap<>(); - this.armor = new HashMap<>(); + public InventoryManager() { + this.inventories = new HashMap<>(); } - public void storeInv(Player p) throws IOException { - // Avoid overwrites - if (items.containsKey(p)) return; - - // Fetch the player's items and armor - ItemStack[] items = p.getInventory().getContents(); - ItemStack[] armor = p.getInventory().getArmorContents(); - - // Store them in memory - this.items.put(p, items); - this.armor.put(p, armor); - - // And on disk - File file = new File(dir, p.getName()); - YamlConfiguration config = new YamlConfiguration(); - config.set("items", items); - config.set("armor", armor); - config.save(file); - - p.updateInventory(); + public void put(Player p, ItemStack[] contents) { + inventories.put(p, contents); } - public void restoreInv(Player p) throws IOException, InvalidConfigurationException { - // Try to grab the items from memory first - ItemStack[] items = this.items.get(p); - ItemStack[] armor = this.armor.get(p); - - // If we can't restore from memory, restore from file - if (items == null || armor == null) { - File file = new File(dir, p.getName()); - - YamlConfiguration config = new YamlConfiguration(); - config.load(file); - - // Get the items and armor lists - List itemsList = config.getList("items"); - List armorList = config.getList("armor"); - - // Turn the lists into arrays - items = itemsList.toArray(new ItemStack[itemsList.size()]); - armor = armorList.toArray(new ItemStack[armorList.size()]); + public void equip(Player p) { + ItemStack[] contents = inventories.get(p); + if (contents == null) { + return; } - - // Set the player inventory contents - p.getInventory().setContents(items); - p.getInventory().setArmorContents(armor); + p.getInventory().setContents(contents); } - public void clearCache(Player p) { - items.remove(p); - armor.remove(p); - - File file = new File(dir, p.getName()); - if (file.exists()) { - file.delete(); - } + public void remove(Player p) { + inventories.remove(p); } /** * Clear a player's inventory completely. * @param p a player */ - public void clearInventory(Player p) { + public static void clearInventory(Player p) { PlayerInventory inv = p.getInventory(); inv.clear(); inv.setHelmet(null); @@ -131,29 +80,23 @@ public class InventoryManager public static boolean restoreFromFile(MobArena plugin, Player p) { try { - // Grab the file and load the config - File dir = new File(plugin.getDataFolder(), "inventories"); - File file = new File(dir, p.getName()); + File inventories = new File(plugin.getDataFolder(), "inventories"); + File file = new File(inventories, p.getUniqueId().toString()); + + if (!file.exists()) { + return false; + } + YamlConfiguration config = new YamlConfiguration(); config.load(file); - // Get the items and armor lists - List itemsList = config.getList("items"); - List armorList = config.getList("armor"); + ItemStack[] contents = config.getList("contents").toArray(new ItemStack[0]); + p.getInventory().setContents(contents); - // Turn the lists into arrays - ItemStack[] items = itemsList.toArray(new ItemStack[itemsList.size()]); - ItemStack[] armor = armorList.toArray(new ItemStack[armorList.size()]); - - // Set the player inventory contents - p.getInventory().setContents(items); - p.getInventory().setArmorContents(armor); - - // Delete files file.delete(); - return true; } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Failed to restore inventory for " + p.getName(), e); return false; } }