From b1c6b618279fd181799aeafdb041ad2d1775a585 Mon Sep 17 00:00:00 2001 From: Andreas Troelsen Date: Sun, 29 Apr 2018 14:42:57 +0200 Subject: [PATCH] Refactor the join/leave process. Note that this commit makes breaking changes to the Arena interface and completely changes the responsibility of the InventoryManager. The join/leave process is replaced with an implementation of the Command Pattern, where every step of the process (e.g. teleport to lobby, change gamemode to survival, reset health, etc.) is realized as an implementation of the new Step interface, which has "run" and "undo" operations. The "run" operation takes a snapshot of a specific part of the player's state and then resets it. The "undo" operation restores the snapshot. Step groupings are arranged in two factory classes, PlayerJoinArena and PlayerSpecArena, which realize, respectively, the join and spectate processes. Each instance of ArenaImpl instantiates groupings for its own context, and a grouping is invoked as one unit when a player joins or spectates an arena, and rolled back when a player leaves. As a result of a more stringent process, some things are now a little different than before: - Setting spectate-on-death to true effectively results in /ma leave followed by /ma spec. This makes the player/arena state a little more predictable and well-defined. - Using exit warps will result in the player leaving to their entry point and then being teleported to the exit warp. This means the exit warp doesn't have any effect on the rest of the restoration process, as it effectively happens "post leave". This finally fixes #423. --- .../com/garbagemule/MobArena/ArenaClass.java | 2 +- .../com/garbagemule/MobArena/ArenaImpl.java | 232 +++++------------- .../com/garbagemule/MobArena/MobArena.java | 36 --- .../com/garbagemule/MobArena/PlayerData.java | 109 -------- .../commands/admin/RestoreCommand.java | 10 +- .../garbagemule/MobArena/framework/Arena.java | 10 - .../MobArena/listeners/MAGlobalListener.java | 2 - .../MobArena/steps/ClearInventory.java | 66 +++++ .../MobArena/steps/ClearPotionEffects.java | 34 +++ .../com/garbagemule/MobArena/steps/Defer.java | 36 +++ .../MobArena/steps/GrantRewards.java | 27 ++ .../MobArena/steps/MovePlayerStep.java | 29 +++ .../MobArena/steps/MoveToExit.java | 14 ++ .../MobArena/steps/MoveToLobby.java | 14 ++ .../MobArena/steps/MoveToSpec.java | 14 ++ .../MobArena/steps/PlayerJoinArena.java | 32 +++ .../MobArena/steps/PlayerMultiStep.java | 70 ++++++ .../MobArena/steps/PlayerSpecArena.java | 31 +++ .../MobArena/steps/PlayerStep.java | 18 ++ .../MobArena/steps/SetExperience.java | 31 +++ .../garbagemule/MobArena/steps/SetFlying.java | 35 +++ .../MobArena/steps/SetGameMode.java | 28 +++ .../garbagemule/MobArena/steps/SetHealth.java | 39 +++ .../garbagemule/MobArena/steps/SetHunger.java | 39 +++ .../MobArena/steps/SetPlayerTime.java | 42 ++++ .../garbagemule/MobArena/steps/SitPets.java | 79 ++++++ .../com/garbagemule/MobArena/steps/Step.java | 28 +++ .../MobArena/steps/StepFactory.java | 11 + .../MobArena/time/TimeStrategy.java | 24 -- .../MobArena/time/TimeStrategyLocked.java | 27 -- .../MobArena/time/TimeStrategyNull.java | 15 -- .../com/garbagemule/MobArena/util/Delays.java | 30 --- .../util/inventory/InventoryManager.java | 105 ++------ 33 files changed, 818 insertions(+), 501 deletions(-) delete mode 100644 src/main/java/com/garbagemule/MobArena/PlayerData.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/ClearInventory.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/ClearPotionEffects.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/Defer.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/GrantRewards.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/MovePlayerStep.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/MoveToExit.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/MoveToLobby.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/MoveToSpec.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/PlayerJoinArena.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/PlayerMultiStep.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/PlayerSpecArena.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/PlayerStep.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetExperience.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetFlying.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetGameMode.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetHealth.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetHunger.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SetPlayerTime.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/SitPets.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/Step.java create mode 100644 src/main/java/com/garbagemule/MobArena/steps/StepFactory.java delete mode 100644 src/main/java/com/garbagemule/MobArena/time/TimeStrategy.java delete mode 100644 src/main/java/com/garbagemule/MobArena/time/TimeStrategyLocked.java delete mode 100644 src/main/java/com/garbagemule/MobArena/time/TimeStrategyNull.java delete mode 100644 src/main/java/com/garbagemule/MobArena/util/Delays.java 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; } }