diff --git a/.factorypath b/.factorypath new file mode 100644 index 0000000..ba34881 --- /dev/null +++ b/.factorypath @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 7099080..b4328e3 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,10 @@ jitpack.io https://jitpack.io + + enginehub-maven + http://maven.enginehub.org/repo/ + @@ -56,6 +60,12 @@ 1.7 provided + + com.sk89q.worldedit + worldedit-bukkit + 7.2.0-SNAPSHOT + provided + redis.clients jedis diff --git a/src/main/java/me/sisko/partygames/DefaultListener.java b/src/main/java/me/sisko/partygames/DefaultListener.java index d975a4e..437683e 100644 --- a/src/main/java/me/sisko/partygames/DefaultListener.java +++ b/src/main/java/me/sisko/partygames/DefaultListener.java @@ -21,7 +21,9 @@ import org.bukkit.event.player.PlayerItemDamageEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import me.sisko.partygames.util.MinigameManager; import net.md_5.bungee.api.ChatColor; /* @@ -34,15 +36,23 @@ public class DefaultListener implements Listener { @EventHandler(priority = EventPriority.LOW) public void onJoin(PlayerJoinEvent e) { - e.getPlayer().sendMessage(ChatColor.GREEN + "Welcome to party games!"); - e.getPlayer().setGameMode(GameMode.SURVIVAL); - e.getPlayer().getInventory().clear(); - FileConfiguration config = Main.getPlugin().getConfig(); - e.getPlayer().teleport(new Location(Main.getWorld(), config.getDouble("spawn.x"), - config.getDouble("spawn.y"), config.getDouble("spawn.z"), (float) config.getDouble("spawn.yaw"), - (float) config.getDouble("spawn.pitch"))); + if(!MinigameManager.addPlayer(e.getPlayer())) { + e.getPlayer().sendMessage(ChatColor.GREEN + "Welcome to party games!"); + e.getPlayer().setGameMode(GameMode.SURVIVAL); + e.getPlayer().getInventory().clear(); + FileConfiguration config = Main.getPlugin().getConfig(); + e.getPlayer().teleport(new Location(Main.getWorld(), config.getDouble("spawn.x"), + config.getDouble("spawn.y"), config.getDouble("spawn.z"), (float) config.getDouble("spawn.yaw"), + (float) config.getDouble("spawn.pitch"))); + } } + @EventHandler(priority = EventPriority.LOW) + public void onLeave(PlayerQuitEvent e) { + MinigameManager.removePlayer(e.getPlayer()); + } + + // by default, disallow block modifications @EventHandler(priority = EventPriority.LOW) public void onBlockBreak(BlockBreakEvent e) { diff --git a/src/main/java/me/sisko/partygames/commands/playCommand.java b/src/main/java/me/sisko/partygames/commands/playCommand.java index d11d890..da311fd 100644 --- a/src/main/java/me/sisko/partygames/commands/playCommand.java +++ b/src/main/java/me/sisko/partygames/commands/playCommand.java @@ -17,7 +17,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St if(MinigameManager.isValidType(args[0])) { MinigameManager.playGame(args[0]); } else { - sender.sendMessage(args[0] + "is not a valid game!"); + sender.sendMessage(args[0] + " is not a valid game!"); } } } diff --git a/src/main/java/me/sisko/partygames/minigames/DiggingMinigame.java b/src/main/java/me/sisko/partygames/minigames/DiggingMinigame.java index 1938687..af3e818 100644 --- a/src/main/java/me/sisko/partygames/minigames/DiggingMinigame.java +++ b/src/main/java/me/sisko/partygames/minigames/DiggingMinigame.java @@ -1,18 +1,17 @@ package me.sisko.partygames.minigames; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; +import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.json.JSONObject; @@ -20,12 +19,9 @@ import me.sisko.partygames.util.MinigameManager; public class DiggingMinigame extends Minigame { - private String name; - private String description; - private String map; private final Material[] block_types = {Material.DIRT, Material.SNOW_BLOCK, - Material.STONE, Material.NETHERRACK, Material.DARK_OAK_LOG, Material.OAK_PLANKS}; + Material.STONE, Material.NETHERRACK, Material.DARK_OAK_LOG, Material.OAK_PLANKS, Material.COBWEB}; private Location winnerLocation; private Location spectatorLocation; @@ -36,24 +32,10 @@ public class DiggingMinigame extends Minigame { private List inGame; private List winners; + private boolean gameStarted; @Override - public String getName() { - return name; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public String getMap() { - return map; - } - - @Override - public boolean jsonValid(JSONObject json) { + public final boolean jsonValid(final JSONObject json) { final String[] keys = {"name", "description", "map", "stack_lowest_block_y", "stack_height", "stacks", "spectator_spawn", "winner_spawn"}; for(final String key : keys) { @@ -63,7 +45,7 @@ public boolean jsonValid(JSONObject json) { } @Override - public void setup(JSONObject json) { + public void setup(final JSONObject json) { name = json.getString("name"); description = json.getString("description"); @@ -99,6 +81,9 @@ public void setup(JSONObject json) { @Override public void initialize() { Random rng = new Random(); + winners = new ArrayList(); + inGame = new ArrayList(); + gameStarted = false; // build all the stacks for(int y = 0; y < height; y++) { @@ -112,34 +97,63 @@ public void initialize() { } @Override - public void start(final List players) { - inGame = new ArrayList(); + public void prestart(final List players) { + List tools = new ArrayList(); + tools.add(new ItemStack(Material.STONE_PICKAXE)); + tools.add(new ItemStack(Material.STONE_AXE)); + tools.add(new ItemStack(Material.STONE_SHOVEL)); + tools.add(new ItemStack(Material.STONE_SWORD)); + Collections.shuffle(tools, new Random()); + for(int i = 0; i < players.size(); i++) { final Player p = players.get(i); - p.teleport(spawns.get(i)); + inGame.add(p); + p.teleport(spawns.get(i)); p.setGameMode(GameMode.SURVIVAL); p.getInventory().clear(); - p.getInventory().addItem(new ItemStack(Material.STONE_PICKAXE)); - p.getInventory().addItem(new ItemStack(Material.STONE_AXE)); - p.getInventory().addItem(new ItemStack(Material.STONE_SHOVEL)); + for(final ItemStack item : tools) { + p.getInventory().addItem(item); + } } + MinigameManager.prestartComplete(); + } + + @Override + public void start() { + gameStarted = true; + } + + @Override + public void postgame() { + gameStarted = false; } @Override public void cleanup() { - // TODO Auto-generated method stub + for(Player p : Bukkit.getOnlinePlayers()) { + p.getInventory().clear(); + } + inGame.clear(); + winners.clear(); + } + @Override + public final List timeout() { + return winners; } - @Override @EventHandler - public void onJoin(PlayerJoinEvent e) { - e.getPlayer().teleport(spectatorLocation); + @Override + public void addPlayer(Player p) { + p.teleport(spectatorLocation); } - @Override @EventHandler - public void onLeave(PlayerQuitEvent e) { - if(inGame.contains(e.getPlayer())) inGame.remove(e.getPlayer()); + @Override + public void removePlayer(Player p) { + if(inGame.contains(p)) inGame.remove(p); + if(inGame.size() == 0 && gameStarted) { + MinigameManager.gameComplete(winners); + } } @EventHandler @@ -149,14 +163,22 @@ public void onBreak(BlockBreakEvent e) { for (Material m : block_types) { if(e.getBlock().getType() == m) allowed = true; } + allowed = allowed && gameStarted; + if(allowed) { e.setCancelled(false); if(e.getBlock().getLocation().getY()-0.5 <= lowestY) { e.getPlayer().teleport(winnerLocation); + e.getPlayer().getInventory().clear(); inGame.remove(e.getPlayer()); winners.add(e.getPlayer()); - + if(gameStarted && (inGame.size() == 0 || winners.size() >= 3)) { + MinigameManager.gameComplete(winners); + } } + + // not sure why this is needed, but it destroys the last block + e.getBlock().setType(Material.AIR); } } else { e.setCancelled(true); diff --git a/src/main/java/me/sisko/partygames/minigames/Minigame.java b/src/main/java/me/sisko/partygames/minigames/Minigame.java index 073e553..9bfbd72 100644 --- a/src/main/java/me/sisko/partygames/minigames/Minigame.java +++ b/src/main/java/me/sisko/partygames/minigames/Minigame.java @@ -3,10 +3,7 @@ import java.util.List; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.json.JSONObject; /* @@ -17,29 +14,72 @@ */ public abstract class Minigame implements Listener { - public abstract String getName(); - public abstract String getDescription(); - public abstract String getMap(); + // must be set properly during setup() + protected String name; + protected String description; + protected String map; + + public final String getName() { + return name; + } + + public final String getDescription() { + return description; + } + + public final String getMap() { + return map; + } + + /* + time before the minigame manager will force end the minigame + used to prevent an afk player or impossible game from soft-locking + the server + override this method to change the default timeout period + */ + public final long getTimeoutTime() { + return 20*60*5; + } // verify a json object contains all needed parameters - public abstract boolean jsonValid(JSONObject json); + public abstract boolean jsonValid(final JSONObject json); // set up the minigame, using parameters passed as a json object // only needs to be called once public abstract void setup(JSONObject json); - // construct the map, get everything prepared, etc before + // construct the map, get everything prepared, etc before game starts + // called once per minigame, must call MinigameManager.initializationComplete() + // exactly once public abstract void initialize(); - // add all players to the minigame and start it - public abstract void start(final List players); + // add all players to the minigame but don't let them do anything yet + public abstract void prestart(final List players); + + // start the minigame, allowing players to take action + public abstract void start(); + + // called after the game is complete, but before the next game is starting + // players should no longer be able to take action. Do not teleport + // players away; this will be done by the next minigame's prestart + // DO NOT clear the winners list, this will screw up scoringh + // clear winners list in cleanup() instead + public abstract void postgame(); // garbage collection etc public abstract void cleanup(); - @EventHandler - public abstract void onJoin(PlayerJoinEvent e); + // called when a game runs out of time, must return a list of current winners + // postgame() will be called immidiately after this is called + public abstract List timeout(); + + // called when someone joins during a game + // this can happen during any stage of the + // game, so be prepared + public abstract void addPlayer(Player p); - @EventHandler - public abstract void onLeave(PlayerQuitEvent e); + // valled when someone leaves a game + // this can happen during any stage of the + // game, so be prepared + public abstract void removePlayer(Player p); } diff --git a/src/main/java/me/sisko/partygames/minigames/TNTRunMinigame.java b/src/main/java/me/sisko/partygames/minigames/TNTRunMinigame.java new file mode 100644 index 0000000..649971c --- /dev/null +++ b/src/main/java/me/sisko/partygames/minigames/TNTRunMinigame.java @@ -0,0 +1,195 @@ +package me.sisko.partygames.minigames; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extension.factory.PatternFactory; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.scheduler.BukkitRunnable; +import org.json.JSONObject; + +import me.sisko.partygames.Main; +import me.sisko.partygames.util.MinigameManager; + +public class TNTRunMinigame extends Minigame { + + private Location spawn; + private List layers; + private int lowestY; + + private List inGame; + private List winners; + private boolean gameStarted; + + @Override + public final boolean jsonValid(final JSONObject json) { + final String[] keys = {"name", "description", "map", "spawn", + "layers"}; + for(final String key : keys) { + if(!json.has(key)) return false; + } + return true; + } + + @Override + public void setup(final JSONObject json) { + + name = json.getString("name"); + description = json.getString("description"); + map = json.getString("map"); + + Main.getPlugin().getLogger().info("Setting up a tnt run map " + map); + + // add the spawn points + final JSONObject spectatorJson = json.getJSONObject("spawn"); + spawn = new Location(Main.getWorld(), spectatorJson.getDouble("x"), + spectatorJson.getDouble("y"), spectatorJson.getDouble("z"), + spectatorJson.getFloat("yaw"), spectatorJson.getFloat("pitch")); + + + lowestY = 255; + layers = new ArrayList(); + for(final Object layer : json.getJSONArray("layers")) { + JSONObject layerJson = (JSONObject) layer; + + final int y = layerJson.getInt("y"); + if(y < lowestY) lowestY = y; + + BlockVector3[] layerLocation = { + BlockVector3.at(layerJson.getInt("x_1"), y, layerJson.getInt("z_1")), + BlockVector3.at(layerJson.getInt("x_2"), y, layerJson.getInt("z_2")) + }; + + layers.add(layerLocation); + } + } + + @Override + public void initialize() { + winners = new ArrayList(); + inGame = new ArrayList(); + gameStarted = false; + + // build the tnt arena + for(BlockVector3[] layer : layers) { + CuboidRegion selection = new CuboidRegion(layer[0], layer[1]); + + try { + Pattern pattern = new PatternFactory(WorldEdit.getInstance()).parseFromInput("minecraft:red_sandstone", new ParserContext()); + WorldEdit.getInstance().newEditSession(new BukkitWorld(Main.getWorld())).setBlocks(selection, pattern); + } catch (InputParseException | MaxChangedBlocksException e) { + Main.getPlugin().getLogger().warning("Could not set the tnt run floor!"); + e.printStackTrace(); + } + + } + + MinigameManager.initializationComplete(); + } + + @Override + public void prestart(final List players) { + for(int i = 0; i < players.size(); i++) { + final Player p = players.get(i); + + inGame.add(p); + p.teleport(spawn); + } + MinigameManager.prestartComplete(); + } + + @Override + public void start() { + gameStarted = true; + } + + @Override + public void postgame() { + gameStarted = false; + } + + @Override + public void cleanup() { + for(Player p : Bukkit.getOnlinePlayers()) { + p.setFlying(false); + p.setAllowFlight(false); + } + inGame.clear(); + winners.clear(); + } + + @Override + public final List timeout() { + for (Player p : inGame) { + winners.add(p); + } + Collections.reverse(winners); + return winners; + } + + @Override + public void addPlayer(Player p) { + p.teleport(spawn); + p.setFlying(true); + p.setAllowFlight(true); + } + + @Override + public void removePlayer(Player p) { + if(inGame.contains(p)) inGame.remove(p); + p.setFlying(false); + p.setAllowFlight(false); + + if(inGame.size() == 0 && gameStarted) { + Collections.reverse(winners); + MinigameManager.gameComplete(winners); + } + } + + @EventHandler + public void onMove(PlayerMoveEvent e) { + if(e.getTo().getY() < lowestY-1) { + e.setCancelled(true); + if(inGame.contains(e.getPlayer()) && gameStarted) { + inGame.remove(e.getPlayer()); + winners.add(e.getPlayer()); + addPlayer(e.getPlayer()); + + if(inGame.size() == 0) { + Collections.reverse(winners); + MinigameManager.gameComplete(winners); + } + } + } else if (inGame.contains(e.getPlayer()) && gameStarted) { + Block below = e.getPlayer().getLocation().add(0, -1, 0).getBlock(); + + // @TODO make block types configurable + // also need to change the initialize() method + if(below.getType().equals(Material.RED_SANDSTONE)) { + below.setType(Material.ORANGE_TERRACOTTA); + new BukkitRunnable() { + @Override + public void run() { + below.setType(Material.AIR); + } + }.runTaskLater(Main.getPlugin(), 20); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/sisko/partygames/util/MinigameManager.java b/src/main/java/me/sisko/partygames/util/MinigameManager.java index d2bc68e..04f0d3f 100644 --- a/src/main/java/me/sisko/partygames/util/MinigameManager.java +++ b/src/main/java/me/sisko/partygames/util/MinigameManager.java @@ -12,13 +12,17 @@ import java.util.stream.Collectors; import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; +import org.bukkit.scheduler.BukkitRunnable; import org.json.JSONObject; import me.sisko.partygames.Main; import me.sisko.partygames.minigames.DiggingMinigame; import me.sisko.partygames.minigames.Minigame; +import me.sisko.partygames.minigames.TNTRunMinigame; public class MinigameManager { // maps a string, representing the minigame type, to a list @@ -26,7 +30,8 @@ public class MinigameManager { // json file. private static Map> minigames; - private static Minigame currentMinigame; + private static Minigame currentMinigame = null; + private static BukkitRunnable timeout = null; public static void load() { @@ -47,6 +52,8 @@ public static void load() { Minigame m = null; if(type.equals("digging")) { m = new DiggingMinigame(); + } else if (type.equals("tntrun")) { + m = new TNTRunMinigame(); } // if type is valid, attempt to construct minigame and place it in the map accordingly @@ -60,6 +67,8 @@ public static void load() { minigames.put(type, new ArrayList()); minigames.get(type).add(m); } + } else { + Main.getPlugin().getLogger().warning("Invalid JSON for this minigame type! Minigame not being loaded."); } } } @@ -81,15 +90,121 @@ public static void playGame(String type) { // called by the minigame when done initializing public static void initializationComplete() { - currentMinigame.start(Main.getPlugin().getServer().getOnlinePlayers().stream().collect(Collectors.toList())); + currentMinigame.prestart(Main.getPlugin().getServer().getOnlinePlayers().stream().collect(Collectors.toList())); Bukkit.getPluginManager().registerEvents(currentMinigame, Main.getPlugin()); } + // called by the minigame when done initializing + public static void prestartComplete() { + Bukkit.broadcastMessage("Game starting in 10 seconds..."); + + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game starting in 3 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 7*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game starting in 2 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 8*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game starting in 1 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 9*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game started!"); + + timeout = new BukkitRunnable(){ + @Override + public void run() { + gameComplete(currentMinigame.timeout()); + } + + }; + timeout.runTaskLater(Main.getPlugin(), currentMinigame.getTimeoutTime()); + currentMinigame.start(); + } + }.runTaskLater(Main.getPlugin(), 10*20); + + } + // called by the minigame when it is done running - public static void gameComplete(List winners) { - currentMinigame.cleanup(); - HandlerList.unregisterAll(currentMinigame); - winners.get(0).sendMessage("You are winner !"); + public static void gameComplete(final List winners) { + timeout.cancel(); + timeout = null; + currentMinigame.postgame(); + + Bukkit.broadcastMessage("Minigame " + currentMinigame.getName() + " complete!"); + Bukkit.broadcastMessage("Map: " + currentMinigame.getMap()); + Bukkit.broadcastMessage("Winners: "); + for(int i = 0; i < winners.size(); i++) { + Bukkit.broadcastMessage(winners.get(i).getDisplayName()); + } + + Bukkit.broadcastMessage("Game ending in 10 seconds..."); + + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game ending in 3 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 7*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game ending in 2 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 8*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game ending in 1 seconds..."); + } + }.runTaskLater(Main.getPlugin(), 9*20); + new BukkitRunnable(){ + @Override + public void run() { + Bukkit.broadcastMessage("Game ended!"); + HandlerList.unregisterAll(currentMinigame); + currentMinigame.cleanup(); + currentMinigame = null; + + for(Player p : Bukkit.getOnlinePlayers()) { + FileConfiguration config = Main.getPlugin().getConfig(); + p.teleport(new Location(Main.getWorld(), config.getDouble("spawn.x"), + config.getDouble("spawn.y"), config.getDouble("spawn.z"), (float) config.getDouble("spawn.yaw"), + (float) config.getDouble("spawn.pitch"))); + } + + } + }.runTaskLater(Main.getPlugin(), 10*20); + } + + public static final boolean addPlayer(Player p) { + if(inGame()) { + currentMinigame.addPlayer(p); + return true; + } + return false; + } + + public static final boolean removePlayer(Player p) { + if(inGame()) { + currentMinigame.removePlayer(p); + return true; + } + return false; + } + + public static final boolean inGame() { + return currentMinigame != null; } private static final JSONObject getJson(File f) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2562b92..6c1c06a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: me.sisko.partygames.Main version: 1.3 api-version: 1.16 author: Captain_Sisko -depend: [Vault, LuckPerms] +depend: [Vault, LuckPerms, Worldedit] commands: play: description: test command