diff --git a/pom.xml b/pom.xml index 7d2b124..5172ef5 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,10 @@ + + WorldEdit Repo + https://maven.enginehub.org/repo/ + codemc-releases https://repo.codemc.io/repository/maven-releases/ @@ -70,6 +74,18 @@ + + com.sk89q.worldedit + worldedit-bukkit + 7.2.10 + provided + + + com.sk89q.worldguard + worldguard-bukkit + 7.0.7 + provided + io.papermc.paper paper-api diff --git a/src/main/java/moe/oko/opennaw/OpenNAW.java b/src/main/java/moe/oko/opennaw/OpenNAW.java index 561bbbf..1928def 100644 --- a/src/main/java/moe/oko/opennaw/OpenNAW.java +++ b/src/main/java/moe/oko/opennaw/OpenNAW.java @@ -1,8 +1,10 @@ package moe.oko.opennaw; import moe.oko.opennaw.command.CityCommand; +import moe.oko.opennaw.command.GateCommand; import moe.oko.opennaw.command.NationCommand; import moe.oko.opennaw.listener.ActionListener; +import moe.oko.opennaw.listener.GateListener; import moe.oko.opennaw.storage.NAWDatabase; import moe.oko.opennaw.util.*; import net.luckperms.api.LuckPerms; @@ -23,6 +25,10 @@ public final class OpenNAW extends JavaPlugin { public CityHandler cityHandler; + public GateHandler gateHandler; + + public TNTHandler tntHandler; + public NAWDatabase database; public ConfigHelper configHelper; @@ -44,6 +50,12 @@ public final class OpenNAW extends JavaPlugin { public CityHandler getCityHandler(){ return cityHandler; } public GroupHandler getGroupHandler() { return groupHandler; } + public GateHandler getGateHandler() { return gateHandler; } + + public TNTHandler getTntHandler() { + return tntHandler; + } + @Override public void onEnable() { // Initialize internals @@ -58,7 +70,9 @@ public final class OpenNAW extends JavaPlugin { actionLogger = new ActionLogger(); nationHandler = new NationHandler(); cityHandler = new CityHandler(); + gateHandler = new GateHandler(); groupHandler = new GroupHandler(this.luckPerms); + tntHandler = new TNTHandler(); info("OpenNAW is initialized."); info("Starting database load."); @@ -67,13 +81,16 @@ public final class OpenNAW extends JavaPlugin { nationHandler.loadNationList(database.loadNations()); database.loadPlayers(); cityHandler.loadCityList(database.loadCities()); + database.loadGates(); //Do not change above load order. // Register Commands & Events this.getCommand("nation").setExecutor(new NationCommand()); this.getCommand("city").setExecutor(new CityCommand()); + this.getCommand("gate").setExecutor(new GateCommand()); getServer().getPluginManager().registerEvents(new ChatHandler(), this); getServer().getPluginManager().registerEvents(new ActionListener(), this); + getServer().getPluginManager().registerEvents(new GateListener(), this); if(Bukkit.getPluginManager().isPluginEnabled("dynmap")) { this.dynmapEnabled = true; @@ -84,7 +101,10 @@ public final class OpenNAW extends JavaPlugin { @Override public void onDisable() { + gateHandler.closeAllGates(); //Close gates before stopping. + tntHandler.shutdown(); info("Starting persistence save."); + gateHandler.saveGates(); cityHandler.saveCities(); nationHandler.saveNations(); info("OpenNAW is disabled!"); diff --git a/src/main/java/moe/oko/opennaw/command/CityCommand.java b/src/main/java/moe/oko/opennaw/command/CityCommand.java index 2a715ec..405ed3a 100644 --- a/src/main/java/moe/oko/opennaw/command/CityCommand.java +++ b/src/main/java/moe/oko/opennaw/command/CityCommand.java @@ -24,7 +24,6 @@ public class CityCommand implements TabExecutor { if (args.length < 1) { return false; } var cityList = OpenNAW.getInstance().getCityHandler().getCityList(); - switch (args[0]) { case "add": { var location = ((Player) sender).getLocation(); diff --git a/src/main/java/moe/oko/opennaw/command/GateCommand.java b/src/main/java/moe/oko/opennaw/command/GateCommand.java new file mode 100644 index 0000000..3e42c1d --- /dev/null +++ b/src/main/java/moe/oko/opennaw/command/GateCommand.java @@ -0,0 +1,164 @@ +package moe.oko.opennaw.command; + +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.model.Gate; +import moe.oko.opennaw.util.CommandHelper; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.block.data.type.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class GateCommand implements TabExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(CommandHelper.NOT_PLAYER); + return true; + } + + if (args.length < 1) { return false; } + var player = (Player) sender; + + switch (args[0]) { + case "create" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the new gate."); + return true; + } + var newGate = new Gate(args[1]); + OpenNAW.getInstance().getGateHandler().getGates().put(newGate.getUuid(), newGate); + player.sendMessage(ChatColor.GREEN + "Successfully created a new gate with the name " + args[1]); + return true; + } + case "delete" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var uid = OpenNAW.getInstance().getGateHandler().getGateUidByName(args[1]); + if(uid != null) { + OpenNAW.getInstance().getGateHandler().getGates().remove(uid); + } else { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + } + return true; + } + case "modify" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var gate = OpenNAW.getInstance().getGateHandler().getGateByName(args[1]); + if(gate == null) { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + return true; + } + try { + var sel = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player)).getSelection(); + //clear old blocks. + gate.getGateBlocks().clear(); + int i = 0; + for(BlockVector3 vec : sel) { + gate.registerGateBlock(new Location(BukkitAdapter.adapt(sel.getWorld()), vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()).getBlock()); + i++; + } + player.sendMessage(ChatColor.GREEN + "Successfully set the new gate area to the " + i + " blocks selected."); + return true; + } catch (Exception e) { + player.sendMessage(ChatColor.RED + "Make sure you have selected a region before defining the gate."); + return true; + } + } + case "open" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var gate = OpenNAW.getInstance().getGateHandler().getGateByName(args[1]); + if(gate == null) { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + return true; + } + gate.open(); + player.sendMessage(ChatColor.GREEN + "Done."); + return true; + } + case "close" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var gate = OpenNAW.getInstance().getGateHandler().getGateByName(args[1]); + if(gate == null) { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + return true; + } + gate.close(); + player.sendMessage(ChatColor.GREEN + "Done."); + return true; + } + case "addsign" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var gate = OpenNAW.getInstance().getGateHandler().getGateByName(args[1]); + if(gate == null) { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + return true; + } + var block = player.getTargetBlockExact(25); + if(!(block.getType().name().contains("_SIGN"))) { + player.sendMessage(ChatColor.RED + "You aren't looking at a sign block..."); + return true; + } + gate.registerSign(block); + player.sendMessage(ChatColor.GREEN + "Successfully registered " + block.getLocation() + " as a sign block for gate " + gate.getName()); + return true; + } + case "delsign" -> { + if(args.length < 2) { + player.sendMessage(ChatColor.RED + "Specify a name for the gate."); + return true; + } + var gate = OpenNAW.getInstance().getGateHandler().getGateByName(args[1]); + if(gate == null) { + player.sendMessage(ChatColor.RED + "That gate is invalid."); + return true; + } + var block = player.getTargetBlockExact(25); + if(!(block.getBlockData() instanceof Sign)) { + player.sendMessage(ChatColor.RED + "You aren't looking at a sign block..."); + return true; + } + if(gate.isGateSign(block.getLocation())) { + gate.removeSignBlock(block); + player.sendMessage(ChatColor.GREEN + "Successfully removed " + block.getLocation() + " as a sign block for gate " + gate.getName()); + } else { + player.sendMessage(ChatColor.RED + "That sign is not a registered sign block...?"); + } + return true; + } + } + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return switch (args.length) { + case 1 -> List.of("create", "delete", "modify", "open", "close", "addsign", "delsign"); + case 2 -> OpenNAW.getInstance().getGateHandler().getGateNames(); + default -> null; + }; + } +} diff --git a/src/main/java/moe/oko/opennaw/listener/GateListener.java b/src/main/java/moe/oko/opennaw/listener/GateListener.java new file mode 100644 index 0000000..27b8823 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/listener/GateListener.java @@ -0,0 +1,34 @@ +package moe.oko.opennaw.listener; + +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.util.WorldGuardHelper; +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; + +public class GateListener implements Listener { + + @EventHandler + public void onBlockInteract(PlayerInteractEvent event) { + if(event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + if(event.getClickedBlock().getType().name().contains("_SIGN")) { + //its a sign!!! + if(OpenNAW.getInstance().getGateHandler().getGateBySign(event.getClickedBlock()) != null) { + if(WorldGuardHelper.fetchCityRegionViaLoc(event.getClickedBlock().getLocation()) != null) { + //in a city, start perm check. + var rgr = WorldGuardHelper.fetchCityRegionViaLoc(event.getClickedBlock().getLocation()); + if(!(WorldGuardHelper.isPlayerPermitted(rgr, event.getPlayer()))) { + event.getPlayer().sendMessage(ChatColor.RED + "" + ChatColor.ITALIC + "The gate is locked..."); + return; + } + } + var gate = OpenNAW.getInstance().getGateHandler().getGateBySign(event.getClickedBlock()); + gate.toggle(); //toggle gate + //TODO PERMS CHECK!!!! + } + } + } + } +} diff --git a/src/main/java/moe/oko/opennaw/listener/TNTListener.java b/src/main/java/moe/oko/opennaw/listener/TNTListener.java new file mode 100644 index 0000000..a67b9d7 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/listener/TNTListener.java @@ -0,0 +1,57 @@ +package moe.oko.opennaw.listener; + +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.util.TNTHandler; +import moe.oko.opennaw.util.WorldGuardHelper; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class TNTListener implements Listener { + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + //Explosives Prevention + if(event.getBlock().getType().equals(Material.TNT)) { + if(event.getBlock().getLocation().subtract(0, 1, 0).getBlock().getType().equals(Material.SOUL_SAND)) { + if(WorldGuardHelper.fetchCityRegionViaLoc(event.getBlock().getLocation()) != null) { + OpenNAW.getInstance().getTntHandler().startExplosive(event.getBlock(), event.getPlayer()); + } else { + event.getPlayer().sendMessage(ChatColor.RED + "" + ChatColor.ITALIC + + "It would be a waste to blow this up here... It's about sending a message isn't it?"); + event.setCancelled(true); + } + } else { + event.getPlayer().sendMessage(ChatColor.RED + "" + ChatColor.ITALIC + + "The ground here isn't sticky enough to hold together the badly constructed explosive. Maybe soul sand would work?"); + event.setCancelled(true); + } + } + } + + @EventHandler + public void onEntityExplosion(EntityExplodeEvent event) { + if(event.getEntity().getType().equals(EntityType.PRIMED_TNT)) { + OpenNAW.getInstance().getTntHandler().detonate(event); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractAtEntityEvent event) { + if(event.getRightClicked().getType().equals(EntityType.PRIMED_TNT)) { + if(WorldGuardHelper.fetchCityRegionViaLoc(event.getRightClicked().getLocation()) != null) { + var rg = WorldGuardHelper.fetchCityRegionViaLoc(event.getRightClicked().getLocation()); + if(WorldGuardHelper.isPlayerPermitted(rg, event.getPlayer())) { + OpenNAW.getInstance().getTntHandler().defuse((TNTPrimed) event.getRightClicked()); + } + } + } + } +} diff --git a/src/main/java/moe/oko/opennaw/model/City.java b/src/main/java/moe/oko/opennaw/model/City.java index c99e131..ee91099 100644 --- a/src/main/java/moe/oko/opennaw/model/City.java +++ b/src/main/java/moe/oko/opennaw/model/City.java @@ -88,7 +88,7 @@ public class City { public boolean startSiege(Nation nation) { if(state.equals(CityState.RECOVERY)) return false; state = CityState.SIEGE; - Bukkit.broadcast(Component.text(ChatColor.RED + nation.getName() + ChatColor.GOLD + " has begun a siege against the city of " + ChatColor.AQUA + getName())); + Bukkit.broadcastMessage(ChatColor.RED + nation.getName() + ChatColor.GOLD + " has begun a siege against the city of " + ChatColor.AQUA + getName()); for(var player : Bukkit.getOnlinePlayers()) { player.playSound(player.getLocation(), Sound.ITEM_TRIDENT_THUNDER, 1F, 1F); } @@ -128,14 +128,16 @@ public class City { public void defend() { //successful defense logic. - Bukkit.broadcast(Component.text(ChatColor.RED + attacker.getName() + ChatColor.GOLD + "'s siege against the city of " - + ChatColor.AQUA + getName() + ChatColor.GOLD + " has " + ChatColor.RED + "failed")); + Bukkit.broadcastMessage(ChatColor.RED + attacker.getName() + ChatColor.GOLD + "'s siege against the city of " + + ChatColor.AQUA + getName() + ChatColor.GOLD + " has " + ChatColor.RED + "failed"); if(health < 250) { health = (short) (health + 50); } OpenNAW.getInstance().getActionLogger().cancelDefenseActions(this); attacker = null; this.state = CityState.RECOVERY; //Start recovery, prohibit sieges until restart. + OpenNAW.getInstance().getLogger().info("Starting regen..."); + OpenNAW.getInstance().getTntHandler().endSiege(this); } public void capture() { @@ -146,7 +148,9 @@ public class City { //Dyn is enabled, we need to do setters. OpenNAW.getInstance().getDynmapHandler().updateCityInfoMarker(this); } - Bukkit.broadcast(Component.text(ChatColor.AQUA + getName() + ChatColor.GOLD + " has been successfully captured by " + ChatColor.RED + this.owner.getName())); + Bukkit.broadcastMessage(ChatColor.AQUA + getName() + ChatColor.GOLD + " has been successfully captured by " + ChatColor.RED + this.owner.getName()); + OpenNAW.getInstance().getLogger().info("Starting regen..."); + OpenNAW.getInstance().getTntHandler().endSiege(this); } public boolean isUnclaimed() { diff --git a/src/main/java/moe/oko/opennaw/model/ExplosivesData.java b/src/main/java/moe/oko/opennaw/model/ExplosivesData.java new file mode 100644 index 0000000..6bee876 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/model/ExplosivesData.java @@ -0,0 +1,30 @@ +package moe.oko.opennaw.model; + +import org.bukkit.Material; +import org.bukkit.block.Block; + +import java.util.HashMap; +import java.util.List; + +public class ExplosivesData { + //The Block destroyed and the material it previously was. + private HashMap blocksDestroyed; + private City city; + + public ExplosivesData(List blocks, City city) { + blocksDestroyed = new HashMap<>(); + this.city = city; + for(var block : blocks) { + blocksDestroyed.put(block, block.getType()); + } + } + + public void regenerate() { + //Regenerate all blocks previously affected. + for(var block : blocksDestroyed.keySet()) { + block.setType(blocksDestroyed.get(block)); + } + } + + public City getCity() { return city; } +} diff --git a/src/main/java/moe/oko/opennaw/model/Gate.java b/src/main/java/moe/oko/opennaw/model/Gate.java new file mode 100644 index 0000000..9e2f765 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/model/Gate.java @@ -0,0 +1,142 @@ +package moe.oko.opennaw.model; + +import moe.oko.opennaw.OpenNAW; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; + +import java.util.HashMap; +import java.util.UUID; + +public class Gate { + private String name; + + private UUID uuid; + + private boolean open; + + private HashMap signs = new HashMap<>(); + private HashMap gateBlocks; + + //New gate constructor. + public Gate(String name) { + this.name = name; + this.uuid = UUID.randomUUID(); + this.open = false; + gateBlocks = new HashMap<>(); + } + + //Db Const + public Gate(UUID uid, String name) { + this.uuid = uid; + this.name = name; + this.open = false; + gateBlocks = new HashMap<>(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public UUID getUuid() { + return uuid; + } + + public HashMap getSigns() { + return signs; + } + + public HashMap getGateBlocks() { + return gateBlocks; + } + + public void registerSign(Block block) { + var gate = new GateBlock(block); + if(signs.containsValue(gate)) { + signs.replace(gate.getId(), gate); + return; + } + signs.put(gate.getId(), gate); + } + + public void registerSignFromDB(GateBlock block) { + if(signs.containsValue(block)) { + signs.replace(block.getId(), block); + return; + } + signs.put(block.getId(), block); + } + + public boolean isGateSign(Location location) { + for(var block : signs.values()) { + if(block.getBlock().getLocation().equals(location)) { return true; } + } + return false; + } + + public void removeSignBlock(Block block) { + for(var blocks : signs.values()) { + if(blocks.getBlock().equals(block)) { + signs.remove(blocks.getId()); + } + } + } + + public boolean registerGateBlock(Block block) { + var newBlock = new GateBlock(block); + if(gateBlocks.containsKey(newBlock)) { return false; } + gateBlocks.put(newBlock, newBlock.getBlock().getType()); + return true; + } + + public void registerGateBlockDb(GateBlock block) { + if(gateBlocks.containsKey(block)) { + signs.replace(block.getId(), block); + return; + } + gateBlocks.put(block, block.getBlock().getType()); + } + + public void toggle() { + if(!open) { + open(); + open = !open; + } else { + close(); + open = !open; + } + } + + public boolean open() { + try { + for (var block : gateBlocks.keySet()) { + block.getBlock().setType(Material.AIR); + block.getBlock().getWorld().playSound(block.getBlock().getLocation(), Sound.BLOCK_PISTON_CONTRACT, 1, 1); + } + return true; + } catch (Exception ex) { + ex.printStackTrace(); + OpenNAW.getInstance().getLogger().severe("Failed to open gate " + name); + return false; + } + } + + public boolean close() { + try { + for (var block : gateBlocks.keySet()) { + block.getBlock().setType(gateBlocks.get(block)); + block.getBlock().getWorld().playSound(block.getBlock().getLocation(), Sound.BLOCK_PISTON_EXTEND, 1, 1); + } + return true; + } catch (Exception ex) { + ex.printStackTrace(); + OpenNAW.getInstance().getLogger().severe("Failed to open gate " + name); + return false; + } + } +} diff --git a/src/main/java/moe/oko/opennaw/model/GateBlock.java b/src/main/java/moe/oko/opennaw/model/GateBlock.java new file mode 100644 index 0000000..1ab3042 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/model/GateBlock.java @@ -0,0 +1,31 @@ +package moe.oko.opennaw.model; + +import org.bukkit.block.Block; + +import java.util.UUID; + +//Used primarily for SQL storage +//Used BOTH for Signs & Actual Openable blocks. +public class GateBlock { + private UUID id; + private Block block; + + public GateBlock(Block block) { + this.block = block; + this.id = UUID.randomUUID(); + } + + //Db Const + public GateBlock(UUID uid, Block block) { + this.id = uid; + this.block = block; + } + + public Block getBlock() { + return block; + } + + public UUID getId() { + return id; + } +} diff --git a/src/main/java/moe/oko/opennaw/storage/NAWDatabase.java b/src/main/java/moe/oko/opennaw/storage/NAWDatabase.java index 687cf07..c3415b4 100644 --- a/src/main/java/moe/oko/opennaw/storage/NAWDatabase.java +++ b/src/main/java/moe/oko/opennaw/storage/NAWDatabase.java @@ -2,8 +2,9 @@ package moe.oko.opennaw.storage; import moe.oko.opennaw.OpenNAW; import moe.oko.opennaw.model.City; +import moe.oko.opennaw.model.Gate; +import moe.oko.opennaw.model.GateBlock; import moe.oko.opennaw.model.Nation; -import moe.oko.opennaw.model.type.CityType; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; @@ -39,10 +40,26 @@ public class NAWDatabase { "`pointx` INT NOT NULL DEFAULT 0," + "`pointy` INT NOT NULL DEFAULT 0," + "`pointz` INT NOT NULL DEFAULT 0) COMMENT='Cities on the NAW map';"; + + private final String INITIALIZE_GATES = "CREATE TABLE IF NOT EXISTS `gates` (" + + "`name` VARCHAR(50) NOT NULL,`uuid` VARCHAR(50) NOT NULL, PRIMARY KEY (`uuid`));"; + + private final String INITIALITE_GATE_SIGNS = "CREATE TABLE IF NOT EXISTS `gateSigns`(" + + "`uuid` VARCHAR(50) NOT NULL, `id` VARCHAR(50) NOT NULL, `world` VARCHAR(50) NOT NULL, `x` INT NOT NULL, `y` INT NOT NULL, `z` INT NOT NULL, PRIMARY KEY (`id`));"; + + private final String INITIALIZE_GATE_BLOCKS = "CREATE TABLE IF NOT EXISTS `gateBlocks`(" + + "`uuid` VARCHAR(50) NOT NULL, `id` VARCHAR(50) NOT NULL, `world` VARCHAR(50) NOT NULL, `x` INT NOT NULL, `y` INT NOT NULL, `z` INT NOT NULL, PRIMARY KEY (`id`));"; + private final String SELECT_CITIES = "SELECT * FROM `cities`;"; private final String SELECT_NATION = "SELECT * FROM `nations`;"; private final String SELECT_PLAYERS = "SELECT * FROM `nation_players`;"; + private final String SELECT_GATES = "SELECT * FROM `gates`;"; + + private final String SELECT_GATE_SIGNS = "SELECT * FROM `gateSigns`;"; + + private final String SELECT_GATE_BLOCKS = "SELECT * FROM `gateBlocks`;"; + //name 1,9; group 2,10; icon 3,11; colour 4, 12; world 5,13; x 6,14; y 7,15; z 8,16; private final String SAVE_NATION = "INSERT INTO `nations`(nation_name,groupName,dynmapIcon, colour, spawnworld,spawnx,spawny,spawnz) VALUES " + "(?,?,?,?,?,?,?,?)" + @@ -51,9 +68,20 @@ public class NAWDatabase { private final String SAVE_PLAYER = "INSERT INTO `nation_players`(nation_name, player_uuid) VALUES (?,?) " + "ON DUPLICATE KEY UPDATE nation_name=?, player_uuid=?;"; //name 1,9; resource 2,10; type 3,11; owner 4,12; world 5,13; x 6,14; y 7,15; z 8,16; - private final String SAVE_CITY = "INSERT INTO `cities`(city_name, resource, type, OWNER, pointworld, pointx, pointy, pointz) VALUES (?,?,?,?,?,?,?,?)" + + private final String SAVE_CITY = "INSERT INTO `cities`(city_name, resource, type, OWNER, pointworld, pointx, pointy, pointz) VALUES (?,?,?,?,?,?,?,?) " + "ON DUPLICATE KEY UPDATE city_name=?, resource=?, type=?, OWNER=?, pointworld=?, pointx=?, pointy=?, pointz=?;"; + //name, 1 3; uuid 2 4; + private final String SAVE_GATE = "INSERT INTO `gates`(name, uuid) VALUES (?,?) " + + "ON DUPLICATE KEY UPDATE name=?, uuid=?;"; + + //uuid 1, 7; id 2, 8; world 3, 9; x 4 10; y 5 11; z 6 12; + private final String SAVE_GATE_SIGN = "INSERT INTO `gateSigns`(uuid, id, world, x, y, z) VALUES (?,?,?,?,?,?)" + + "ON DUPLICATE KEY UPDATE uuid=?, id=?, world=?, x=?, y=?, z=?;"; + + private final String SAVE_GATE_BLOCK = "INSERT INTO `gateBlocks`(uuid, id, world, x, y, z) VALUES (?,?,?,?,?,?) " + + "ON DUPLICATE KEY UPDATE uuid=?, id=?, world=?, x=?, y=?, z=?;"; + public NAWDatabase(String username, String password, String host, int port, String database) { //all relevant try { @@ -75,6 +103,12 @@ public class NAWDatabase { nationsInit.execute(); PreparedStatement cityInit = connection.prepareStatement(INITIALIZE_CITIES); cityInit.execute(); + PreparedStatement gatesInit = connection.prepareStatement(INITIALIZE_GATES); + gatesInit.execute(); + PreparedStatement gatesInit2 = connection.prepareStatement(INITIALITE_GATE_SIGNS); + gatesInit2.execute(); + PreparedStatement gatesInit3 = connection.prepareStatement(INITIALIZE_GATE_BLOCKS); + gatesInit3.execute(); } catch (Exception ex) { ex.printStackTrace(); OpenNAW.getInstance().getLogger().severe("Failed to initialize tables for sql."); @@ -234,4 +268,112 @@ public class NAWDatabase { OpenNAW.getInstance().getLogger().severe("Failed to save cities."); } } + + public void loadGates() { + try { + List gateLoad = new ArrayList<>(); + PreparedStatement gateload = connection.prepareStatement(SELECT_GATES); + ResultSet rs = gateload.executeQuery(); + int i = 0; + while(rs.next()) { + var newGate = new Gate(UUID.fromString(rs.getString(2)), rs.getString(1)); + OpenNAW.getInstance().getGateHandler().getGates().put(newGate.getUuid(), newGate); + i++; + } + OpenNAW.getInstance().getLogger().info("Loaded " + i + " gates."); + PreparedStatement gateSignLoad = connection.prepareStatement(SELECT_GATE_SIGNS); + ResultSet rs2 = gateSignLoad.executeQuery(); + i = 0; + while(rs2.next()) { + var gate = OpenNAW.getInstance().getGateHandler().getGateByUuid(UUID.fromString(rs2.getString(1))); + //uuid 1, 7; id 2, 8; world 3, 9; x 4 10; y 5 11; z 6 12; + gate.registerSignFromDB(new GateBlock(UUID.fromString(rs2.getString(2)), + new Location(Bukkit.getWorld(rs2.getString(3)), rs2.getInt(4) + , rs2.getInt(5), rs2.getInt(6)).getBlock())); + i++; + } + OpenNAW.getInstance().getLogger().info("Loaded " + i + " signs for gates."); + PreparedStatement gateBlockLoad = connection.prepareStatement(SELECT_GATE_BLOCKS); + ResultSet rs3 = gateBlockLoad.executeQuery(); + i = 0; + while(rs3.next()) { + var gate = OpenNAW.getInstance().getGateHandler().getGateByUuid(UUID.fromString(rs3.getString(1))); + gate.registerGateBlockDb(new GateBlock(UUID.fromString(rs3.getString(2)), + new Location(Bukkit.getWorld(rs3.getString(3)), rs3.getInt(4) + , rs3.getInt(5), rs3.getInt(6)).getBlock())); + i++; + } + OpenNAW.getInstance().getLogger().info("Loaded " + i + " blocks for gates."); + } catch (Exception ex) { + ex.printStackTrace(); + OpenNAW.getInstance().getLogger().severe("Failed to save gates."); + } + } + + public void saveGates(Collection gates) { + try { + OpenNAW.getInstance().getLogger().info("Starting gate save."); + //name, 1 3; uid 2, 4; + //for signs and blocks uuid 1, 7; id 2, 8; world 3, 9; x 4 10; y 5 11; z 6 12; + PreparedStatement gateSave = connection.prepareStatement(SAVE_GATE); + PreparedStatement gateSignSave = connection.prepareStatement(SAVE_GATE_SIGN); + PreparedStatement gateBlockSave = connection.prepareStatement(SAVE_GATE_BLOCK); + int g = 0; + for(var gate : gates) { + int i = 0; + gateSave.setString(1, gate.getName()); + gateSave.setString(3, gate.getName()); + gateSave.setString(2, gate.getUuid().toString()); + gateSave.setString(4, gate.getUuid().toString()); + gateSave.execute(); + for(var sign : gate.getSigns().values()) { + //sign save + gateSignSave.setString(1, gate.getUuid().toString()); //UUID for gate identification (so we know where + gateSignSave.setString(7, gate.getUuid().toString()); //to put blocks on load). + gateSignSave.setString(2, sign.getId().toString()); //Id's to prevent duplicates in the DB + gateSignSave.setString(8, sign.getId().toString()); + var loc = sign.getBlock().getLocation(); + gateSignSave.setString(3, loc.getWorld().getName()); + gateSignSave.setString(9, loc.getWorld().getName()); + gateSignSave.setInt(4, loc.getBlockX()); + gateSignSave.setInt(10, loc.getBlockX()); + gateSignSave.setInt(5, loc.getBlockY()); + gateSignSave.setInt(11, loc.getBlockY()); + gateSignSave.setInt(6, loc.getBlockZ()); + gateSignSave.setInt(12, loc.getBlockZ()); + gateSignSave.addBatch(); + i++; + } + gateSignSave.executeBatch(); + OpenNAW.getInstance().getLogger().info("Saved " + i + " signs for gate " + gate.getName()); + i = 0; + //Now we save all blocks + for(var block : gate.getGateBlocks().keySet()) { + //sign save + gateBlockSave.setString(1, gate.getUuid().toString()); //UUID for gate identification (so we know where + gateBlockSave.setString(7, gate.getUuid().toString()); //to put blocks on load). + gateBlockSave.setString(2, block.getId().toString()); //Id's to prevent duplicates in the DB + gateBlockSave.setString(8, block.getId().toString()); + var loc = block.getBlock().getLocation(); + gateBlockSave.setString(3, loc.getWorld().getName()); + gateBlockSave.setString(9, loc.getWorld().getName()); + gateBlockSave.setInt(4, loc.getBlockX()); + gateBlockSave.setInt(10, loc.getBlockX()); + gateBlockSave.setInt(5, loc.getBlockY()); + gateBlockSave.setInt(11, loc.getBlockY()); + gateBlockSave.setInt(6, loc.getBlockZ()); + gateBlockSave.setInt(12, loc.getBlockZ()); + gateBlockSave.addBatch(); + i++; + } + gateBlockSave.executeBatch(); + OpenNAW.getInstance().getLogger().info("Saved " + i + " blocks for gate " + gate.getName()); + g++; + } + OpenNAW.getInstance().getLogger().info("Saved " + g + " gates."); + } catch (Exception ex) { + ex.printStackTrace(); + OpenNAW.getInstance().getLogger().severe("Failed to save gates."); + } + } } diff --git a/src/main/java/moe/oko/opennaw/util/ChatHandler.java b/src/main/java/moe/oko/opennaw/util/ChatHandler.java index 91c7553..bca58a9 100644 --- a/src/main/java/moe/oko/opennaw/util/ChatHandler.java +++ b/src/main/java/moe/oko/opennaw/util/ChatHandler.java @@ -26,12 +26,17 @@ public class ChatHandler implements Listener { e.setCancelled(true); + /* + Kyori support broken? audience = Audience.audience(Bukkit.getOnlinePlayers()); audience.sendMessage(prefix - .append(player.displayName().color(TextColor.fromHexString("#939597"))) + .append(Component.text(player.getDisplayName())) + .color(TextColor.fromHexString("#939597")) .append(Component.text(": ")) .append(message)); + */ + Bukkit.broadcastMessage(prefix + player.getDisplayName() + TextColor.fromHexString("#939597") + ": " + message); } } diff --git a/src/main/java/moe/oko/opennaw/util/GateHandler.java b/src/main/java/moe/oko/opennaw/util/GateHandler.java new file mode 100644 index 0000000..790c2d5 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/util/GateHandler.java @@ -0,0 +1,79 @@ +package moe.oko.opennaw.util; + +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.model.Gate; +import org.bukkit.block.Block; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +public class GateHandler { + private HashMap gates = new HashMap<>(); + + public GateHandler() { + } + + public Gate getGateByUuid(UUID uid) { + return gates.get(uid); + } + + public Gate getGateBySign(Block block) { + for(var gate : gates.values()) { + for(var sign : gate.getSigns().values()) { + if(sign.getBlock().equals(block)) { + return gate; + } + } + } + return null; //no gate. + } + + public UUID getGateUidByName(String name) { + for(var gate : gates.values()) { + if(gate.getName().equals(name)) { + return gate.getUuid(); + } + } + return null; + } + + public Gate getGateByName(String name) { + for(var gate : gates.values()) { + if(gate.getName().equals(name)) { + return gate; + } + } + return null; + } + + public HashMap getGates() { + return gates; + } + + public List getGateNames() { + List names = new ArrayList<>(); + for(var gate : gates.values()) { + names.add(gate.getName()); + } + return names; + } + + public void setGates(HashMap gates) { + this.gates = gates; + } + + public void closeAllGates() { + var i = 0; + for(var gate : gates.values()) { + gate.close(); + i++; + } + OpenNAW.getInstance().getLogger().info("Closed " + i + " gates."); + } + + public void saveGates() { + OpenNAW.getInstance().getDatabase().saveGates(gates.values()); + } +} diff --git a/src/main/java/moe/oko/opennaw/util/GuiHelper.java b/src/main/java/moe/oko/opennaw/util/GuiHelper.java index ded5f95..ea424ad 100644 --- a/src/main/java/moe/oko/opennaw/util/GuiHelper.java +++ b/src/main/java/moe/oko/opennaw/util/GuiHelper.java @@ -35,7 +35,7 @@ public class GuiHelper { for(var nation : OpenNAW.getInstance().getNationHandler().getNationList()) { var is = new ItemStack(Material.getMaterial(nation.getColour() + "_WOOL")); var meta = is.getItemMeta(); - meta.displayName(Component.text(ChatColor.valueOf(nation.getColour()) + nation.getName())); + meta.setDisplayName(ChatColor.valueOf(nation.getColour()) + nation.getName()); is.setItemMeta(meta); menu.setButton(s, new ItemButton<>(is) { @Override @@ -64,7 +64,7 @@ public class GuiHelper { var colour = new ItemStack(Material.valueOf(nation.getColour() + "_WOOL")); var meta = colour.getItemMeta(); - meta.displayName(Component.text(colour.getType().name().replace("_WOOL", "").toUpperCase())); + meta.setDisplayName(colour.getType().name().replace("_WOOL", "").toUpperCase()); colour.setItemMeta(meta); var woolCollection = new ArrayList<>(); for(var mat : Material.values()) { @@ -84,7 +84,7 @@ public class GuiHelper { var player = (Player) event.getViewers().get(0); var is = (ItemStack) woolCollection.get(i); var meta = is.getItemMeta(); - meta.displayName(Component.text(is.getType().name().replace("_WOOL", "").toUpperCase())); + meta.setDisplayName(is.getType().name().replace("_WOOL", "").toUpperCase()); is.setItemMeta(meta); setIcon(is); nation.setColour(this.getIcon().getType().name().replace("_WOOL", "")); @@ -98,7 +98,7 @@ public class GuiHelper { var player = (Player) event.getViewers().get(0); var is = (ItemStack) woolCollection.get(i); var meta = is.getItemMeta(); - meta.displayName(Component.text(is.getType().name().replace("_WOOL", "").toUpperCase())); + meta.setDisplayName(is.getType().name().replace("_WOOL", "").toUpperCase()); is.setItemMeta(meta); setIcon(is); nation.setColour(this.getIcon().getType().name().replace("_WOOL", "")); @@ -111,10 +111,10 @@ public class GuiHelper { */ var is = new ItemStack(Material.BARRIER); var meta1 = is.getItemMeta(); - meta1.displayName(Component.text("Spawn Location")); - var lore = new ArrayList(); - lore.add(Component.text(ChatColor.GRAY + "Click to set your current location as the spawn for this nation.")); - meta1.lore(lore); + meta1.setDisplayName("Spawn Location"); + var lore = new ArrayList(); + lore.add(ChatColor.GRAY + "Click to set your current location as the spawn for this nation."); + meta1.setLore(lore); is.setItemMeta(meta1); menu.setButton(1, new ItemButton<>(is) { @Override @@ -136,10 +136,10 @@ public class GuiHelper { var typeis = new ItemStack(map.get(city.getType())); var typemeta = typeis.getItemMeta(); - typemeta.displayName(Component.text(city.getType().name())); - List list = new ArrayList<>(); - list.add(Component.text(city.getType().getDescription())); - typemeta.lore(list); + typemeta.setDisplayName(city.getType().name()); + List list = new ArrayList<>(); + list.add(city.getType().getDescription()); + typemeta.setLore(list); typeis.setItemMeta(typemeta); menu.setButton(0, new CycleButton<>(typeis, map.keySet().stream().toList(), 0) { @Override @@ -149,10 +149,10 @@ public class GuiHelper { var player = (Player) event.getViewers().get(0); var typeis = new ItemStack(map.get(map.keySet().stream().toList().get(i))); var typemeta = typeis.getItemMeta(); - typemeta.displayName(Component.text(map.keySet().stream().toList().get(i).name())); - List list = new ArrayList<>(); - list.add(Component.text(map.keySet().stream().toList().get(i).getDescription())); - typemeta.lore(list); + typemeta.setDisplayName(map.keySet().stream().toList().get(i).name()); + List list = new ArrayList<>(); + list.add(map.keySet().stream().toList().get(i).getDescription()); + typemeta.setLore(list); typeis.setItemMeta(typemeta); setIcon(typeis); city.setType(map.keySet().stream().toList().get(i)); @@ -166,10 +166,10 @@ public class GuiHelper { var player = (Player) event.getViewers().get(0); var typeis = new ItemStack(map.get(map.keySet().stream().toList().get(i))); var typemeta = typeis.getItemMeta(); - typemeta.displayName(Component.text(map.keySet().stream().toList().get(i).name())); - List list = new ArrayList<>(); - list.add(Component.text(map.keySet().stream().toList().get(i).getDescription())); - typemeta.lore(list); + typemeta.setDisplayName(map.keySet().stream().toList().get(i).name()); + List list = new ArrayList<>(); + list.add(map.keySet().stream().toList().get(i).getDescription()); + typemeta.setLore(list); typeis.setItemMeta(typemeta); setIcon(typeis); city.setType(map.keySet().stream().toList().get(i)); @@ -179,7 +179,7 @@ public class GuiHelper { var nameis = new ItemStack(Material.OAK_SIGN); var namemeta = nameis.getItemMeta(); - namemeta.displayName(Component.text("Set City Name")); + namemeta.setDisplayName("Set City Name"); nameis.setItemMeta(namemeta); menu.setButton(1, new ItemButton<>(nameis) { @Override diff --git a/src/main/java/moe/oko/opennaw/util/TNTHandler.java b/src/main/java/moe/oko/opennaw/util/TNTHandler.java new file mode 100644 index 0000000..5f58f40 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/util/TNTHandler.java @@ -0,0 +1,71 @@ +package moe.oko.opennaw.util; + +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.model.City; +import moe.oko.opennaw.model.ExplosivesData; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.entity.EntityExplodeEvent; + +import java.util.*; + +public class TNTHandler { + List explosiveSave = new ArrayList<>(); + List pendingExplosives = new ArrayList<>(); + + public void startExplosive(Block block, Player player) { + var city = OpenNAW.getInstance().getCityHandler().fetchCityViaName(WorldGuardHelper.fetchCityRegionViaLoc(block.getLocation()).getId()); + block.setType(Material.AIR); + if(WorldGuardHelper.isPlayerPermitted(WorldGuardHelper.fetchCityRegionViaLoc(block.getLocation()), player)) { + //Betrayal has its benefits... (10 Second explosive time) + player.sendMessage(ChatColor.YELLOW + "" + ChatColor.ITALIC + "Your knowledge of the city allows you to evade the guards who may call out for help..."); + var tnt = (TNTPrimed) block.getWorld().spawnEntity(block.getLocation(), EntityType.PRIMED_TNT, false); + tnt.setFuseTicks(200); + pendingExplosives.add(tnt); + } else { + //Normal start. (60 Second explosive time + warning) + Bukkit.broadcastMessage(ChatColor.RED + player.getDisplayName() + + ChatColor.GOLD + " has placed an improvised explosive at the walls of " + ChatColor.AQUA + city.getName() + ChatColor.GOLD + "!"); + var tnt = (TNTPrimed) block.getWorld().spawnEntity(block.getLocation(), EntityType.PRIMED_TNT, false); + tnt.setFuseTicks(1200); + pendingExplosives.add(tnt); + } + } + + public void defuse(TNTPrimed tnt) { + pendingExplosives.remove(tnt); + var loc = tnt.getLocation(); + tnt.remove(); //Delete tnt + loc.getWorld().spawnParticle(Particle.WHITE_ASH, loc, 1000, .5, .5, .5); + } + + public void detonate(EntityExplodeEvent event) { + var city = OpenNAW.getInstance().getCityHandler().fetchCityViaName(WorldGuardHelper.fetchCityRegionViaLoc(event.getLocation()).getId()); + explosiveSave.add(new ExplosivesData(event.blockList(), city)); + pendingExplosives.remove((TNTPrimed) event.getEntity()); + } + + public void endSiege(City city) { + for (var explosive : explosiveSave) { + if(explosive.getCity().equals(city)) { + explosive.regenerate(); + explosiveSave.remove(explosive); + } + } + } + + public void shutdown() { + for(var data : explosiveSave) { + data.regenerate(); //Regen all before shutdown of server. + } + for(var tnt : pendingExplosives) { + tnt.remove(); //Remove all tnt + } + } +} diff --git a/src/main/java/moe/oko/opennaw/util/WorldGuardHelper.java b/src/main/java/moe/oko/opennaw/util/WorldGuardHelper.java new file mode 100644 index 0000000..c777336 --- /dev/null +++ b/src/main/java/moe/oko/opennaw/util/WorldGuardHelper.java @@ -0,0 +1,46 @@ +package moe.oko.opennaw.util; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import moe.oko.opennaw.OpenNAW; +import moe.oko.opennaw.model.Nation; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class WorldGuardHelper { + + /* + Used to set the owner when a city is captured namely. + */ + public static void setRegionOwner(ProtectedRegion region, Nation nation) { + region.getMembers().clear(); //Clear prior owner. + region.getMembers().addGroup(nation.getGroup().getName()); + } + + /* + Used to determine whether a player can open a gate, or BETRAY epicly. + */ + public static boolean isPlayerPermitted(ProtectedRegion region, Player player) { + var nation = OpenNAW.getInstance().getNationHandler().getNationByPlayer(player.getUniqueId()); + if(region.getMembers().getGroups().contains(nation.getGroup().getName())) { + return true; + } else { + return false; + } + } + + /* + This will ONLY return city regions. DO NOT LET CITY REGIONS OVERLAP, THIS CAN CAUSE A LOT OF BUGS. + */ + public static ProtectedRegion fetchCityRegionViaLoc(Location location) { + var regionsOnLoc = WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().getApplicableRegions(BukkitAdapter.adapt(location)).getRegions(); + var cityStrs = OpenNAW.getInstance().getCityHandler().getCityList(); + for(var protreg : regionsOnLoc) { + if(cityStrs.contains(protreg.getId())) { + return protreg; + } + } + return null; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 837b7c3..fe321c2 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,7 +4,7 @@ main: moe.oko.opennaw.OpenNAW api-version: 1.18 prefix: OpenNAW softdepend: [dynmap] -depend: [GuiLib] +depend: [GuiLib, WorldEdit, WorldGuard] authors: [ oko ] description: Open source recreation of Nations at War website: https://oko.moe @@ -15,4 +15,8 @@ commands: city: description: Manages cities usage: / + permission: naw.admin + gate: + description: Manages gates + usage: / permission: naw.admin \ No newline at end of file