diff --git a/src/main/java/net/tylermurphy/hideAndSeek/Main.java b/src/main/java/net/tylermurphy/hideAndSeek/Main.java index 12e4bf4..656f60d 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/Main.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/Main.java @@ -23,6 +23,9 @@ import net.tylermurphy.hideAndSeek.command.*; import net.tylermurphy.hideAndSeek.command.map.*; import net.tylermurphy.hideAndSeek.command.map.blockhunt.Enabled; import net.tylermurphy.hideAndSeek.command.map.set.*; +import net.tylermurphy.hideAndSeek.command.world.Create; +import net.tylermurphy.hideAndSeek.command.world.Delete; +import net.tylermurphy.hideAndSeek.command.world.Tp; import net.tylermurphy.hideAndSeek.configuration.*; import net.tylermurphy.hideAndSeek.database.Database; import net.tylermurphy.hideAndSeek.game.*; @@ -39,6 +42,7 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -56,16 +60,28 @@ public class Main extends JavaPlugin implements Listener { private EntityHider entityHider; private Game game; private CommandGroup commandGroup; + private boolean loaded; public void onEnable() { + + long start = System.currentTimeMillis(); + + getLogger().info("Loading Kenshin's Hide and Seek"); Main.instance = this; - this.updateVersion(); + + getLogger().info("Getting minecraft version..."); + this.updateVersion();; try { + getLogger().info("Loading config.yml..."); Config.loadConfig(); + getLogger().info("Loading maps.yml..."); Maps.loadMaps(); + getLogger().info("Loading localization.yml..."); Localization.loadLocalization(); + getLogger().info("Loading items.yml..."); Items.loadItems(); + getLogger().info("Loading leaderboard.yml..."); Leaderboard.loadLeaderboard(); } catch (Exception e) { getLogger().severe(e.getMessage()); @@ -73,12 +89,18 @@ public class Main extends JavaPlugin implements Listener { return; } + getLogger().info("Creating internal scoreboard..."); this.board = new Board(); + getLogger().info("Connecting to database..."); this.database = new Database(); + getLogger().info("Loading disguises..."); this.disguiser = new Disguiser(); + getLogger().info("Loading entity hider..."); this.entityHider = new EntityHider(this, EntityHider.Policy.BLACKLIST); + getLogger().info("Registering listeners..."); this.registerListeners(); + getLogger().info("Registering commands..."); this.commandGroup = new CommandGroup("hs", new Help(), new Reload(), @@ -111,21 +133,38 @@ public class Main extends JavaPlugin implements Listener { new Debug(), new GoTo() ), + new CommandGroup("world", + new Create(), + new Delete(), + new net.tylermurphy.hideAndSeek.command.world.List(), + new Tp() + ), new SetExitLocation(), new Top(), - new Wins() + new Wins(), + new Confirm() ); + getLogger().info("Loading game..."); game = new Game(null, board); + getLogger().info("Scheduling tick tasks..."); getServer().getScheduler().runTaskTimer(this, this::onTick,0,1).getTaskId(); + getLogger().info("Registering outgoing bungeecord plugin channel..."); Bukkit.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); + getLogger().info("Checking for PlaceholderAPI..."); if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) { + getLogger().info("PlaceholderAPI found..."); + getLogger().info("Registering PlaceholderAPI expansion..."); new PAPIExpansion().register(); } + long end = System.currentTimeMillis(); + getLogger().info("Finished loading plugin ("+(end-start)+"ms)"); + loaded = true; + } public void onDisable() { @@ -222,5 +261,24 @@ public class Main extends JavaPlugin implements Listener { public boolean supports(int v){ return version >= v; } + + public java.util.List getWorlds() { + java.util.List worlds = new ArrayList<>(); + File[] containers = getWorldContainer().listFiles(); + if(containers != null) { + Arrays.stream(containers).forEach(file -> { + if (!file.isDirectory()) return; + String[] files = file.list(); + if (files == null) return; + if (!Arrays.asList(files).contains("session.lock") && !Arrays.asList(files).contains("level.dat")) return; + worlds.add(file.getName()); + }); + } + return worlds; + } + + public boolean isLoaded() { + return loaded; + } } \ No newline at end of file diff --git a/src/main/java/net/tylermurphy/hideAndSeek/command/Confirm.java b/src/main/java/net/tylermurphy/hideAndSeek/command/Confirm.java new file mode 100644 index 0000000..a25185d --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/command/Confirm.java @@ -0,0 +1,62 @@ +package net.tylermurphy.hideAndSeek.command; + +import net.tylermurphy.hideAndSeek.command.util.ICommand; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Consumer; + +import static net.tylermurphy.hideAndSeek.configuration.Config.errorPrefix; +import static net.tylermurphy.hideAndSeek.configuration.Localization.message; + +public class Confirm implements ICommand { + + public static final Map confirmations = new HashMap<>(); + + public void execute(Player sender, String[] args) { + Confirmation confirmation = confirmations.get(sender.getUniqueId()); + confirmations.remove(sender.getUniqueId()); + if(confirmation == null) { + sender.sendMessage(errorPrefix + message("NO_CONFIRMATION")); + } else { + long now = System.currentTimeMillis(); + float secs = (now - confirmation.start) / 1000F; + if(secs > 10) { + sender.sendMessage(errorPrefix + message("CONFIRMATION_TIMED_OUT")); + return; + } + confirmation.callback.accept(confirmation.data); + } + } + + public String getLabel() { + return "confirm"; + } + + public String getUsage() { + return ""; + } + + public String getDescription() { + return "Confirm another command if required"; + } + + public List autoComplete(@NotNull String parameter, @NotNull String typed) { + return null; + } + + public static class Confirmation { + public final Consumer callback; + public final String data; + public final long start; + + public Confirmation(String data, Consumer callback) { + this.callback = callback; + this.data = data; + this.start = System.currentTimeMillis(); + } + + } + +} diff --git a/src/main/java/net/tylermurphy/hideAndSeek/command/world/Create.java b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Create.java new file mode 100644 index 0000000..5ab9039 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Create.java @@ -0,0 +1,66 @@ +package net.tylermurphy.hideAndSeek.command.world; + +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.command.util.ICommand; +import net.tylermurphy.hideAndSeek.util.Location; +import org.bukkit.WorldType; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static net.tylermurphy.hideAndSeek.configuration.Config.errorPrefix; +import static net.tylermurphy.hideAndSeek.configuration.Config.messagePrefix; +import static net.tylermurphy.hideAndSeek.configuration.Localization.message; + +public class Create implements ICommand { + + public void execute(Player sender, String[] args) { + List worlds = Main.getInstance().getWorlds(); + if(worlds.contains(args[0])) { + sender.sendMessage(errorPrefix + message("WORLD_EXISTS").addAmount(args[0])); + } + WorldType type; + if(args[1].equals("normal")) { + type = WorldType.NORMAL; + } else if(args[1].equals("flat")) { + type = WorldType.FLAT; + } else { + sender.sendMessage(errorPrefix + message("INVALID_WORLD_TYPE").addAmount(args[1])); + return; + } + + Location temp = new Location(args[0], 0, 0, 0); + + if (temp.load(type) == null) { + sender.sendMessage(errorPrefix + message("WORLD_ADDED_FAILED")); + } else { + sender.sendMessage(messagePrefix + message("WORLD_ADDED").addAmount(args[0])); + } + + } + + public String getLabel() { + return "create"; + } + + public String getUsage() { + return " "; + } + + public String getDescription() { + return "Create a new world"; + } + + public List autoComplete(@NotNull String parameter, @NotNull String typed) { + if(parameter.equals("name")) { + return Collections.singletonList("name"); + } + if(parameter.equals("type")) { + return Arrays.asList("normal", "flat"); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/net/tylermurphy/hideAndSeek/command/world/Delete.java b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Delete.java new file mode 100644 index 0000000..4800e08 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Delete.java @@ -0,0 +1,74 @@ +package net.tylermurphy.hideAndSeek.command.world; + +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.command.Confirm; +import net.tylermurphy.hideAndSeek.command.util.ICommand; +import net.tylermurphy.hideAndSeek.world.WorldLoader; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +import static net.tylermurphy.hideAndSeek.configuration.Config.errorPrefix; +import static net.tylermurphy.hideAndSeek.configuration.Config.messagePrefix; +import static net.tylermurphy.hideAndSeek.configuration.Localization.message; + +public class Delete implements ICommand { + + public void execute(Player sender, String[] args) { + java.util.List worlds = Main.getInstance().getWorlds(); + if(!worlds.contains(args[0])) { + sender.sendMessage(errorPrefix + message("WORLD_DOESNT_EXIST").addAmount(args[0])); + } + + Confirm.Confirmation confirmation = new Confirm.Confirmation(args[0], world -> { + java.util.List worlds_now = Main.getInstance().getWorlds(); + if(!worlds_now.contains(world)) { + sender.sendMessage(errorPrefix + message("WORLD_DOESNT_EXIST").addAmount(world)); + } + World bukkit_world = Bukkit.getWorld(world); + if(bukkit_world != null && bukkit_world.getPlayers().size() > 0) { + sender.sendMessage(errorPrefix + message("WORLD_NOT_EMPTY")); + return; + } + String path = Main.getInstance().getWorldContainer().getPath() + File.separator + world; + if (!Bukkit.getServer().unloadWorld(world, false)) { + sender.sendMessage(errorPrefix + message("WORLD_REMOVED_FAILED")); + return; + } + try { + WorldLoader.deleteDirectory(new File(path)); + } catch (Exception e) { + sender.sendMessage(errorPrefix + message("WORLD_REMOVED_FAILED")); + return; + } + sender.sendMessage(messagePrefix + message("WORLD_REMOVED").addAmount(world)); + }); + + Confirm.confirmations.put(sender.getUniqueId(), confirmation); + sender.sendMessage(messagePrefix + message("CONFIRMATION")); + + } + + public String getLabel() { + return "delete"; + } + + public String getUsage() { + return ""; + } + + public String getDescription() { + return "Delete a world"; + } + + public List autoComplete(@NotNull String parameter, @NotNull String typed) { + if(parameter.equals("name")) { + return Main.getInstance().getWorlds(); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/net/tylermurphy/hideAndSeek/command/world/List.java b/src/main/java/net/tylermurphy/hideAndSeek/command/world/List.java new file mode 100644 index 0000000..2c0f745 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/command/world/List.java @@ -0,0 +1,46 @@ +package net.tylermurphy.hideAndSeek.command.world; + +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.command.util.ICommand; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import static net.tylermurphy.hideAndSeek.configuration.Config.errorPrefix; +import static net.tylermurphy.hideAndSeek.configuration.Config.messagePrefix; +import static net.tylermurphy.hideAndSeek.configuration.Localization.message; + +public class List implements ICommand { + + public void execute(Player sender, String[] args) { + java.util.List worlds = Main.getInstance().getWorlds(); + if(worlds.isEmpty()) { + sender.sendMessage(errorPrefix + message("NO_WORLDS")); + } else { + StringBuilder response = new StringBuilder(messagePrefix + message("LIST_WORLDS")); + for (String world : worlds) { + boolean loaded = Bukkit.getWorld(world) != null; + response.append("\n ").append(world).append(": ").append(loaded ? ChatColor.GREEN + "LOADED" : ChatColor.YELLOW + "NOT LOADED").append(ChatColor.WHITE); + } + sender.sendMessage(response.toString()); + } + } + + public String getLabel() { + return "list"; + } + + public String getUsage() { + return ""; + } + + public String getDescription() { + return "List all worlds in the server"; + } + + public java.util.List autoComplete(@NotNull String parameter, @NotNull String typed) { + return null; + } + +} diff --git a/src/main/java/net/tylermurphy/hideAndSeek/command/world/Tp.java b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Tp.java new file mode 100644 index 0000000..b166297 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/command/world/Tp.java @@ -0,0 +1,49 @@ +package net.tylermurphy.hideAndSeek.command.world; + +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.command.util.ICommand; +import net.tylermurphy.hideAndSeek.util.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static net.tylermurphy.hideAndSeek.configuration.Config.errorPrefix; +import static net.tylermurphy.hideAndSeek.configuration.Localization.message; + +public class Tp implements ICommand { + public void execute(Player sender, String[] args) { + Location test = new Location(args[0], 0, 0,0); + if(!test.exists()) { + sender.sendMessage(errorPrefix + message("WORLD_DOESNT_EXIT")); + return; + } + World world = test.load(); + if(world == null) { + sender.sendMessage(errorPrefix + message("WORLD_LOAD_FAILED")); + return; + } + Location loc = new Location(world.getName(), world.getSpawnLocation()); + loc.teleport(sender); + } + + public String getLabel() { + return "tp"; + } + + public String getUsage() { + return ""; + } + + public String getDescription() { + return "Teleport to another world"; + } + + public List autoComplete(@NotNull String parameter, @NotNull String typed) { + if(parameter.equals("world")) { + return Main.getInstance().getWorlds(); + } + return null; + } +} diff --git a/src/main/java/net/tylermurphy/hideAndSeek/configuration/ConfigManager.java b/src/main/java/net/tylermurphy/hideAndSeek/configuration/ConfigManager.java index eda8470..be699ec 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/configuration/ConfigManager.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/configuration/ConfigManager.java @@ -318,11 +318,11 @@ public class ConfigManager { int index = 0; for(String part : parts) { if (index == 0) { - index = yaml.indexOf(part + ":", index); + index = yaml.indexOf("\n" + part + ":", index) + 1; } else { index = yaml.indexOf(" " + part + ":", index) + 1; } - if (index == -1) break; + if (index == 0) break; } return index; } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/configuration/Maps.java b/src/main/java/net/tylermurphy/hideAndSeek/configuration/Maps.java index 98ba4a0..d691fa9 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/configuration/Maps.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/configuration/Maps.java @@ -3,6 +3,7 @@ package net.tylermurphy.hideAndSeek.configuration; import java.util.*; import java.util.stream.Collectors; +import net.tylermurphy.hideAndSeek.Main; import net.tylermurphy.hideAndSeek.util.Location; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; @@ -72,6 +73,7 @@ public class Maps { ConfigurationSection data = maps.getConfigurationSection(name); if(data == null) return null; Map map = new Map(name); + Main.getInstance().getLogger().info("Loading map: " + name + "..."); map.setSpawn(getSpawn(data, "game")); map.setLobby(getSpawn(data, "lobby")); map.setSeekerLobby(getSpawn(data, "seeker")); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/database/Database.java b/src/main/java/net/tylermurphy/hideAndSeek/database/Database.java index e7bfb26..d2fb40d 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/database/Database.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/database/Database.java @@ -45,8 +45,10 @@ public class Database { public Database(){ if(databaseType.equals("SQLITE")) { + Main.getInstance().getLogger().info("SQLITE database chosen"); connection = new SQLiteConnection(); } else { + Main.getInstance().getLogger().info("MYSQL database chosen"); connection = new MySQLConnection(); } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/database/connections/MySQLConnection.java b/src/main/java/net/tylermurphy/hideAndSeek/database/connections/MySQLConnection.java index b7c1b1d..56f53f6 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/database/connections/MySQLConnection.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/database/connections/MySQLConnection.java @@ -21,6 +21,7 @@ package net.tylermurphy.hideAndSeek.database.connections; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import net.tylermurphy.hideAndSeek.Main; import java.sql.Connection; import java.sql.SQLException; @@ -35,6 +36,13 @@ public class MySQLConnection implements DatabaseConnection { HikariConfig config = new HikariConfig(); + Main.getInstance().getLogger().info("Database host: " + databaseHost); + Main.getInstance().getLogger().info("Database port: " + databasePort); + Main.getInstance().getLogger().info("Database user: " + databaseUser); + Main.getInstance().getLogger().info("Database pass: xxxxxxxxxxx"); + Main.getInstance().getLogger().info("Database name: " + databaseName); + + config.setJdbcUrl("jdbc:mariadb://"+databaseHost+":"+databasePort+"/"+databaseName); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/util/Location.java b/src/main/java/net/tylermurphy/hideAndSeek/util/Location.java index 2abdb9b..efc4329 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/util/Location.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/util/Location.java @@ -4,6 +4,7 @@ import net.tylermurphy.hideAndSeek.Main; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.WorldCreator; +import org.bukkit.WorldType; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -42,13 +43,30 @@ public class Location { this.z = z; } - public World load() { + public Location(@NotNull String world, @NotNull org.bukkit.Location location) { + this.world = world; + this.x = location.getX(); + this.y = location.getY(); + this.z = location.getZ(); + } + + public World load(WorldType type) { World bukkitWorld = Bukkit.getWorld(world); if(bukkitWorld != null) return bukkitWorld; - Bukkit.getServer().createWorld(new WorldCreator(world)); + if (type == null) { + Bukkit.getServer().createWorld(new WorldCreator(world)); + } else { + Bukkit.getServer().createWorld(new WorldCreator(world).type(type)); + } return Bukkit.getWorld(world); } + public World load() { + if(!exists()) return null; + if(!Main.getInstance().isLoaded()) return null; + return load(null); + } + private org.bukkit.Location toBukkit() { return new org.bukkit.Location( Bukkit.getWorld(world), diff --git a/src/main/java/net/tylermurphy/hideAndSeek/world/WorldLoader.java b/src/main/java/net/tylermurphy/hideAndSeek/world/WorldLoader.java index b3be907..6ea1773 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/world/WorldLoader.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/world/WorldLoader.java @@ -155,7 +155,7 @@ public class WorldLoader { out.close(); } - private void deleteDirectory(File directoryToBeDeleted) { + public static void deleteDirectory(File directoryToBeDeleted) { File[] allContents = directoryToBeDeleted.listFiles(); if (allContents != null) { for (File file : allContents) { diff --git a/src/main/resources/lang/localization_en-US.yml b/src/main/resources/lang/localization_en-US.yml index d62153d..61d562b 100644 --- a/src/main/resources/lang/localization_en-US.yml +++ b/src/main/resources/lang/localization_en-US.yml @@ -107,6 +107,20 @@ Localization: BLOCKHUNT_BLOCK_REMOVED: "Removed {AMOUNT} from blockhunt config." BLOCKHUNT_LIST_BLOCKS: "The following blockhunt blocks are:" NO_BLOCKS: "There are no blockhunt blocks in this map." + WORLD_EXISTS: "A world named {AMOUNT} already exists." + WORLD_DOESNT_EXIST: "There is no world named {AMOUNT} that exists." + WORLD_ADDED: "Created a world named {AMOUNT}." + WORLD_ADDED_FAILED: "Failed to create new world." + WORLD_REMOVED: "Deleted a world named {AMOUNT}." + WORLD_REMOVED_FAILED: "Failed to delete world." + WORLD_NOT_EMPTY: "World must be empty to be deleted." + LIST_WORLDS: "The following worlds are:" + NO_WORLDS: "Failed to fetch any worlds." + WORLD_LOAD_FAILED: "Failed to load world." + INVALID_WORLD_TYPE: "Invalid world type: {AMOUNT}." + NO_CONFIRMATION: "You have nothing to confirm." + CONFIRMATION_TIMED_OUT: "The confirmation has timed out." + CONFIRMATION: "Run /hs confirm within 10s to confirm." # DO NOT EDIT IT OR IT MAY BREAK OR RESET FILE version: 4 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 92c6483..c6ce455 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -62,4 +62,14 @@ permissions: default: op hs.map.blockhunt.list: default: op + hs.world.create: + default: op + hs.world.delete: + default: op + hs.world.list: + default: op + hs.world.tp: + default: op + hs.confirm: + default: op