From 458d2268c59aacb3997b2608bc0d55395d78093c Mon Sep 17 00:00:00 2001 From: TheMrEngMan <68214507+TheMrEngMan@users.noreply.github.com> Date: Wed, 13 Jan 2021 01:01:42 -0500 Subject: [PATCH 1/2] Fixed leaderboards not working if used on the UHC server itself Added option to use local SQLite database instead of MySQL (currently auto-used if MySQL details are not changed from the default) Added paperlib to pom.xml --- pom.xml | 10 + .../com/gmail/mezymc/stats/StatsManager.java | 38 +-- .../stats/database/SQLiteConnector.java | 224 ++++++++++++++++++ .../stats/listeners/UhcStatListener.java | 11 + .../stats/scoreboards/BoardPosition.java | 19 +- .../mezymc/stats/scoreboards/LeaderBoard.java | 25 +- 6 files changed, 301 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java diff --git a/pom.xml b/pom.xml index f8868fc..0b757e7 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,10 @@ placeholderapi http://repo.extendedclip.com/content/repositories/placeholderapi/ + + papermc + https://papermc.io/repo/repository/maven-public/ + @@ -63,6 +67,12 @@ 2.9.2 provided + + io.papermc + paperlib + 1.0.4 + compile + \ No newline at end of file diff --git a/src/main/java/com/gmail/mezymc/stats/StatsManager.java b/src/main/java/com/gmail/mezymc/stats/StatsManager.java index a00def1..7fcb241 100644 --- a/src/main/java/com/gmail/mezymc/stats/StatsManager.java +++ b/src/main/java/com/gmail/mezymc/stats/StatsManager.java @@ -1,9 +1,6 @@ package com.gmail.mezymc.stats; -import com.gmail.mezymc.stats.database.DatabaseColumn; -import com.gmail.mezymc.stats.database.DatabaseConnector; -import com.gmail.mezymc.stats.database.MySqlConnector; -import com.gmail.mezymc.stats.database.Position; +import com.gmail.mezymc.stats.database.*; import com.gmail.mezymc.stats.listeners.ConnectionListener; import com.gmail.mezymc.stats.listeners.GuiListener; import com.gmail.mezymc.stats.listeners.StatCommandListener; @@ -180,19 +177,28 @@ boolean loadConfig(){ return false; } - // SQL Details not yet set, disable plugin + boolean useMySql = true; + + // SQL Details not yet set, use SQLite database instead if (cfg.getString("sql.password", "password123").equals("password123")){ - Bukkit.getLogger().warning("[UhcStats] SQL details not set! Disabling plugin!"); - return false; + Bukkit.getLogger().info("[UhcStats] MySQL details not set, using SQLite database instead"); + useMySql = false; } - databaseConnector = new MySqlConnector( - cfg.getString("sql.ip", "localhost"), - cfg.getString("sql.username", "localhost"), - cfg.getString("sql.password", "password123"), - cfg.getString("sql.database", "minecraft"), - cfg.getInt("sql.port", 3306) - ); + // Connect to MySQL database if details are set + if(useMySql) { + databaseConnector = new MySqlConnector( + cfg.getString("sql.ip", "localhost"), + cfg.getString("sql.username", "localhost"), + cfg.getString("sql.password", "password123"), + cfg.getString("sql.database", "minecraft"), + cfg.getInt("sql.port", 3306) + ); + } + // Connect to local SQLite database otherwise + else { + databaseConnector = new SQLiteConnector(); + } onlineMode = cfg.getBoolean("online-mode", true); @@ -239,10 +245,8 @@ boolean loadConfig(){ Bukkit.getLogger().info("[UhcStats] Loaded " + gameModes.size() + " GameModes!"); - Bukkit.getScheduler().runTaskLater(UhcStats.getPlugin(), () -> loadLeaderBoards(cfg), 10); } - // Load server GameMode if (isUhcServer){ if (gameModes.size() == 1){ @@ -380,7 +384,7 @@ private void pushGameModeStats(StatsPlayer statsPlayer, GameMode gameMode){ databaseConnector.pushStats(statsPlayer.getId(), gameMode, statsPlayer.getGameModeStats(gameMode)); } - private void loadLeaderBoards(YamlConfiguration cfg){ + public void loadLeaderBoards(){ ConfigurationSection cfgSection = cfg.getConfigurationSection("leaderboards"); leaderBoards = new HashSet<>(); diff --git a/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java b/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java new file mode 100644 index 0000000..6850689 --- /dev/null +++ b/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java @@ -0,0 +1,224 @@ +package com.gmail.mezymc.stats.database; + +import com.gmail.mezymc.stats.GameMode; +import com.gmail.mezymc.stats.StatType; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; + +import java.io.File; +import java.io.IOException; +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import static com.gmail.mezymc.stats.UhcStats.getPlugin; + +public class SQLiteConnector implements DatabaseConnector { + + private Connection connection; + + public SQLiteConnector() { + connection = null; + } + + @Override + public List getTop10(StatType statType, GameMode gameMode) { + try { + Connection connection = getSqlConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT `id`, `" + statType.getColumnName() + "` FROM `" + gameMode.getTableName() + "` ORDER BY `" + statType.getColumnName() + "` DESC LIMIT 10"); + List positions = new ArrayList<>(); + + int pos = 1; + while (resultSet.next()) { + Position position = new Position( + pos, + resultSet.getString("id"), + resultSet.getInt(statType.getColumnName()) + ); + + positions.add(position); + pos++; + } + + resultSet.close(); + statement.close(); + + return positions; + } catch (SQLException ex) { + ex.printStackTrace(); + return new ArrayList<>(); + } + } + + @Override + public boolean doesTableExists(String tableName) { + Connection connection; + Statement statement; + try { + connection = getSqlConnection(); + statement = connection.createStatement(); + } catch (SQLException ex) { + ex.printStackTrace(); + throw new RuntimeException("Failed to create statement!"); + } + + try { + statement.executeQuery("SELECT 1 FROM `" + tableName + "` LIMIT 1;").close(); + statement.close(); + return true; + } catch (SQLException ex) { + return false; + } + } + + @Override + public void createTable(String name, DatabaseColumn... databaseColumns) { + StringBuilder sb = new StringBuilder("CREATE TABLE `" + name + "` ("); + boolean first = true; + + for (DatabaseColumn databaseColumn : databaseColumns) { + if (first) { + first = false; + } else { + sb.append(", "); + } + + sb.append(databaseColumn.toString()); + } + sb.append(");"); + + try { + Connection connection = getSqlConnection(); + Statement statement = connection.createStatement(); + statement.execute(sb.toString()); + statement.close(); + } catch (SQLException ex) { + Bukkit.getLogger().warning("[UhcStats] Failed to create table!"); + ex.printStackTrace(); + } + } + + @Override + public void pushStats(String playerId, GameMode gameMode, Map stats) { + try { + Connection connection = getSqlConnection(); + Statement statement = connection.createStatement(); + for (StatType statType : stats.keySet()) { + statement.executeUpdate( + "UPDATE `" + gameMode.getTableName() + "` SET `" + statType.getColumnName() + "`=" + stats.get(statType) + " WHERE `id`='" + playerId + "'" + ); + } + statement.close(); + } catch (SQLException ex) { + Bukkit.getLogger().warning("[UhcStats] Failed to push stats for: " + playerId); + ex.printStackTrace(); + } + } + + @Override + public Map loadStats(String playerId, GameMode gameMode) { + Map stats = getEmptyStatMap(); + + try { + Connection connection = getSqlConnection(); + Statement statement = connection.createStatement(); + ResultSet result = statement.executeQuery("SELECT * FROM `" + gameMode.getTableName() + "` WHERE `id`='" + playerId + "'"); + + if (result.next()) { + // collect stats + for (StatType statType : StatType.values()) { + stats.put(statType, result.getInt(statType.getColumnName())); + } + } else { + // Player not found, insert player to table. + insertPlayerToTable(connection, playerId, gameMode); + } + + result.close(); + statement.close(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + + return stats; + } + + @Override + public boolean checkConnection() { + try { + getSqlConnection(); + return true; + } catch (SQLException ex) { + ex.printStackTrace(); + return false; + } + } + + private Connection getSqlConnection() throws SQLException { + Validate.isTrue(!Bukkit.isPrimaryThread(), "You may only open an connection to the database on a asynchronous thread!"); + + // Open connection to local SQLite database "stats.db" + File dataFile = new File(getPlugin().getDataFolder(), "stats.db"); + if (!dataFile.exists()) { + try { + dataFile.createNewFile(); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "File write error: stats.db"); + } + } + try { + if (connection != null && !connection.isClosed()) { + return connection; + } + Class.forName("org.sqlite.JDBC"); + connection = DriverManager.getConnection("jdbc:sqlite:" + dataFile); + return connection; + } catch (SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "SQLite exception on initialize", ex); + } catch (ClassNotFoundException ex) { + Bukkit.getLogger().log(Level.SEVERE, "You need the SQLite JBDC library."); + } + + return null; + } + + private void insertPlayerToTable(Connection connection, String playerId, GameMode gameMode) { + try { + StringBuilder sb = new StringBuilder("INSERT INTO `" + gameMode.getTableName() + "` (`id`"); + for (StatType statType : StatType.values()) { + sb.append(", `" + statType.getColumnName() + "`"); + } + + sb.append(") VALUES ('" + playerId + "'"); + + for (int i = 0; i < StatType.values().length; i++) { + sb.append(", 0"); + } + + sb.append(")"); + + Statement statement = connection.createStatement(); + statement.execute(sb.toString()); + + statement.close(); + } catch (SQLException ex) { + Bukkit.getLogger().warning("[UhcStats] Failed to update stats for: " + playerId); + ex.printStackTrace(); + } + } + + private Map getEmptyStatMap() { + Map stats = new HashMap<>(); + + for (StatType statType : StatType.values()) { + stats.put(statType, 0); + } + + return stats; + } + +} \ No newline at end of file diff --git a/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java b/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java index 0d047dc..44c17d3 100644 --- a/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java +++ b/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java @@ -1,7 +1,9 @@ package com.gmail.mezymc.stats.listeners; import com.gmail.mezymc.stats.*; +import com.gmail.val59000mc.events.UhcGameStateChangedEvent; import com.gmail.val59000mc.events.UhcWinEvent; +import com.gmail.val59000mc.game.GameState; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -46,4 +48,13 @@ public void onGameWin(UhcWinEvent e){ Bukkit.getScheduler().runTaskAsynchronously(UhcStats.getPlugin(), () -> statsManager.pushAllStats()); } + @EventHandler + public void onGameStateChangedEvent(UhcGameStateChangedEvent e){ + // When the game is waiting for players (world have finished being generated), load the leaderboards + // This is so that the leaderboards can go in the correct world + if(e.getNewGameState() == GameState.WAITING) { + Bukkit.getScheduler().runTaskLater(UhcStats.getPlugin(), () -> StatsManager.getStatsManager().loadLeaderBoards(), 10); + } + } + } \ No newline at end of file diff --git a/src/main/java/com/gmail/mezymc/stats/scoreboards/BoardPosition.java b/src/main/java/com/gmail/mezymc/stats/scoreboards/BoardPosition.java index bfeb1ab..c84cffd 100644 --- a/src/main/java/com/gmail/mezymc/stats/scoreboards/BoardPosition.java +++ b/src/main/java/com/gmail/mezymc/stats/scoreboards/BoardPosition.java @@ -72,7 +72,10 @@ public void updateText() { } public void remove(){ - armorStand.remove(); + // Only remove armor stand if it was created in the first place + if(armorStand != null) { + armorStand.remove(); + } } public Location getLocation(){ @@ -98,11 +101,17 @@ public boolean ownsArmorStand(ArmorStand armorStand){ private void spawnArmorStand(){ Location location = getLocation(); - for (Entity entity : location.getWorld().getNearbyEntities(location,1,1,1)){ - if (entity.getType() == EntityType.ARMOR_STAND && entity.getLocation().equals(location)){ - armorStand = (ArmorStand) entity; + // getNearbyEntities() must be used synchronously + LeaderboardUpdateThread.runSync(new Runnable() { + @Override + public void run() { + for (Entity entity : location.getWorld().getNearbyEntities(location, 1, 1, 1)){ + if (entity.getType() == EntityType.ARMOR_STAND && entity.getLocation().equals(location)){ + armorStand = (ArmorStand) entity; + } + } } - } + }); if (armorStand == null){ LeaderboardUpdateThread.runSync(new Runnable() { diff --git a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java index 8d1272d..62192bc 100644 --- a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java +++ b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java @@ -3,9 +3,7 @@ import com.gmail.mezymc.stats.GameMode; import com.gmail.mezymc.stats.StatType; import com.gmail.mezymc.stats.StatsManager; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; +import org.bukkit.*; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; @@ -41,8 +39,27 @@ public Location getLocation() { public void instantiate(ConfigurationSection cfg){ + // Search for correct world to place the leaderboard into + World mainUHCWorld = Bukkit.getWorld("world"); + boolean didFindWorld = false; + for(World world : Bukkit.getWorlds()) { + // The world must be in the overworld but not the default "world" + if(!world.getName().equals("world") && world.getEnvironment() == World.Environment.NORMAL) { + // The correct one is the one with the glass box (the default lobby) + if(world.getBlockAt(0, 199, 0).getType() == Material.GLASS) { + mainUHCWorld = world; + didFindWorld = true; + } + } + } + // If the correct world could not be found, issue a warning + if(!didFindWorld) { + Bukkit.getLogger().warning("[UhcStats] Could not find world for leaderboard"); + } + + // Use the correct world in the location specification location = new Location( - Bukkit.getWorld(cfg.getString("location.world")), + mainUHCWorld, cfg.getDouble("location.x"), cfg.getDouble("location.y"), cfg.getDouble("location.z") From db570f1078c451a03844ac0cf0fa249120ae3969 Mon Sep 17 00:00:00 2001 From: TheMrEngMan <68214507+TheMrEngMan@users.noreply.github.com> Date: Wed, 13 Jan 2021 03:23:48 -0500 Subject: [PATCH 2/2] Added support to automatically place leaderboards in main UHC server lobby, or in a preset world, depending if plugin is used on a UHC server or lobby server Added config option for database type Added config option for leaderboards update interval Added more info in comments of config file Renamed SQLite database file from "stats.db" to "database.db" Added null checks to prevent NPE's when disabling plugin if some leaderboards could not be created --- .../com/gmail/mezymc/stats/StatsManager.java | 28 +++++++--- .../java/com/gmail/mezymc/stats/UhcStats.java | 8 ++- .../stats/database/SQLiteConnector.java | 6 +-- .../stats/listeners/UhcStatListener.java | 12 +++-- .../mezymc/stats/scoreboards/LeaderBoard.java | 51 ++++++++++++------- .../scoreboards/LeaderboardUpdateThread.java | 7 ++- src/main/resources/config.yml | 41 +++++++++++++-- 7 files changed, 115 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/gmail/mezymc/stats/StatsManager.java b/src/main/java/com/gmail/mezymc/stats/StatsManager.java index 7fcb241..7128520 100644 --- a/src/main/java/com/gmail/mezymc/stats/StatsManager.java +++ b/src/main/java/com/gmail/mezymc/stats/StatsManager.java @@ -40,6 +40,7 @@ public class StatsManager{ private boolean isUhcServer; private boolean onlineMode; private GameMode serverGameMode; + private int leaderBoardsUpdateInterval; public StatsManager(){ statsManager = this; @@ -177,15 +178,15 @@ boolean loadConfig(){ return false; } - boolean useMySql = true; + boolean useMySql = cfg.getBoolean ("use-mysql-database"); - // SQL Details not yet set, use SQLite database instead - if (cfg.getString("sql.password", "password123").equals("password123")){ - Bukkit.getLogger().info("[UhcStats] MySQL details not set, using SQLite database instead"); - useMySql = false; + // If need to use MySQL but database connection details are not yet set, disable plugin + if (useMySql && cfg.getString("sql.password", "password123").equals("password123")){ + Bukkit.getLogger().severe("[UhcStats] MySQL database details not set! Disabling plugin since stats cannot be saved!"); + return false; } - // Connect to MySQL database if details are set + // Connect to MySQL database if configured to do so and details are set if(useMySql) { databaseConnector = new MySqlConnector( cfg.getString("sql.ip", "localhost"), @@ -195,7 +196,7 @@ boolean loadConfig(){ cfg.getInt("sql.port", 3306) ); } - // Connect to local SQLite database otherwise + // Connect to local SQLite database if configured to do so else { databaseConnector = new SQLiteConnector(); } @@ -245,6 +246,13 @@ boolean loadConfig(){ Bukkit.getLogger().info("[UhcStats] Loaded " + gameModes.size() + " GameModes!"); + // If this is not a UHC server (but a lobby server with this plugin installed on it), + // load the leader-boards right away since we do not need to wait for the UHC game world(s) to pre-generate + if(!isUhcServer) { + Bukkit.getScheduler().runTaskLater(UhcStats.getPlugin(), () -> loadLeaderBoards(), 10); + } + // Otherwise, they will be loaded when the game is ready + } // Load server GameMode @@ -258,6 +266,8 @@ boolean loadConfig(){ Bukkit.getLogger().info("[UhcStats] Server GameMode is: " + serverGameMode.getKey()); } + leaderBoardsUpdateInterval = cfg.getInt("leaderboards-update-interval", 60); + return true; } @@ -417,4 +427,8 @@ public Set getLeaderBoards(){ return leaderBoards; } + public int getLeaderBoardsUpdateInterval() { return leaderBoardsUpdateInterval; } + + public boolean getIsUhcServer() { return isUhcServer; } + } \ No newline at end of file diff --git a/src/main/java/com/gmail/mezymc/stats/UhcStats.java b/src/main/java/com/gmail/mezymc/stats/UhcStats.java index 01ddf47..a147d56 100644 --- a/src/main/java/com/gmail/mezymc/stats/UhcStats.java +++ b/src/main/java/com/gmail/mezymc/stats/UhcStats.java @@ -35,8 +35,12 @@ public void onEnable(){ @Override public void onDisable() { - for (LeaderBoard leaderBoard : StatsManager.getStatsManager().getLeaderBoards()){ - leaderBoard.unload(); + if(StatsManager.getStatsManager().getLeaderBoards() != null) { + for (LeaderBoard leaderBoard : StatsManager.getStatsManager().getLeaderBoards()) { + if(leaderBoard != null) { + leaderBoard.unload(); + } + } } } diff --git a/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java b/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java index 6850689..a467a80 100644 --- a/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java +++ b/src/main/java/com/gmail/mezymc/stats/database/SQLiteConnector.java @@ -161,13 +161,13 @@ public boolean checkConnection() { private Connection getSqlConnection() throws SQLException { Validate.isTrue(!Bukkit.isPrimaryThread(), "You may only open an connection to the database on a asynchronous thread!"); - // Open connection to local SQLite database "stats.db" - File dataFile = new File(getPlugin().getDataFolder(), "stats.db"); + // Open connection to local SQLite database "database.db" + File dataFile = new File(getPlugin().getDataFolder(), "database.db"); if (!dataFile.exists()) { try { dataFile.createNewFile(); } catch (IOException e) { - Bukkit.getLogger().log(Level.SEVERE, "File write error: stats.db"); + Bukkit.getLogger().log(Level.SEVERE, "File write error: database.db"); } } try { diff --git a/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java b/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java index 44c17d3..bbc06a5 100644 --- a/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java +++ b/src/main/java/com/gmail/mezymc/stats/listeners/UhcStatListener.java @@ -50,10 +50,14 @@ public void onGameWin(UhcWinEvent e){ @EventHandler public void onGameStateChangedEvent(UhcGameStateChangedEvent e){ - // When the game is waiting for players (world have finished being generated), load the leaderboards - // This is so that the leaderboards can go in the correct world - if(e.getNewGameState() == GameState.WAITING) { - Bukkit.getScheduler().runTaskLater(UhcStats.getPlugin(), () -> StatsManager.getStatsManager().loadLeaderBoards(), 10); + // If this is a UHC server (and not a lobby server with this plugin installed on it), + // load the leader-boards only after the the UHC game world(s) are pre-generated + if(StatsManager.getStatsManager().getIsUhcServer()) { + // When the game is waiting for players (world(s) have finished being pre-generated), load the leaderboards + // This is so that the leaderboards can go in the correct world if the default lobby is used + if (e.getNewGameState() == GameState.WAITING) { + Bukkit.getScheduler().runTaskLater(UhcStats.getPlugin(), () -> StatsManager.getStatsManager().loadLeaderBoards(), 10); + } } } diff --git a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java index 62192bc..e6cbbb4 100644 --- a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java +++ b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderBoard.java @@ -39,35 +39,49 @@ public Location getLocation() { public void instantiate(ConfigurationSection cfg){ - // Search for correct world to place the leaderboard into - World mainUHCWorld = Bukkit.getWorld("world"); + String title = cfg.getString("title"); + format = cfg.getString("lines"); + boolean didFindWorld = false; - for(World world : Bukkit.getWorlds()) { - // The world must be in the overworld but not the default "world" - if(!world.getName().equals("world") && world.getEnvironment() == World.Environment.NORMAL) { - // The correct one is the one with the glass box (the default lobby) - if(world.getBlockAt(0, 199, 0).getType() == Material.GLASS) { - mainUHCWorld = world; - didFindWorld = true; + World worldForLeaderboard = Bukkit.getWorlds().get(0); + String configWorldName = cfg.getString("location.world"); + + // If this leader-board specifies which world it should be created in, create it in that world + if(configWorldName != null) { + worldForLeaderboard = Bukkit.getWorld(configWorldName); + if(worldForLeaderboard == null) { + Bukkit.getLogger().warning("[UhcStats] World \"" + configWorldName + "\" for leaderboard titled \"" + title + "\" is invalid"); + Bukkit.getLogger().warning("[UhcStats] Will attempt to place it in default lobby world..."); + } else { + didFindWorld = true; + } + } + // Otherwise, search for the world containing the default lobby to place the leader-board into + if(!didFindWorld) { + for (World world : Bukkit.getWorlds()) { + // The world must be in the overworld + if (world.getEnvironment() == World.Environment.NORMAL) { + // The correct one is the one with the glass box (the default lobby) + if (world.getBlockAt(0, 199, 0).getType() == Material.GLASS) { + worldForLeaderboard = world; + didFindWorld = true; + } } } } - // If the correct world could not be found, issue a warning + // If the correct world could not be found for leader-board, issue a warning if(!didFindWorld) { - Bukkit.getLogger().warning("[UhcStats] Could not find world for leaderboard"); + Bukkit.getLogger().warning("[UhcStats] Could not find the default lobby world for leaderboard titled \"" + title + "\""); + Bukkit.getLogger().warning("[UhcStats] Using the first available one instead"); } - // Use the correct world in the location specification location = new Location( - mainUHCWorld, + worldForLeaderboard, cfg.getDouble("location.x"), cfg.getDouble("location.y"), cfg.getDouble("location.z") ); - String title = cfg.getString("title"); - format = cfg.getString("lines"); - title = ChatColor.translateAlternateColorCodes('&', title); format = ChatColor.translateAlternateColorCodes('&', format); @@ -76,6 +90,7 @@ public void instantiate(ConfigurationSection cfg){ title ); armorStand2 = null; + } public String getFormat(){ @@ -105,7 +120,9 @@ private ArmorStand spawnArmorStand(Location location, String text){ } public void unload(){ - armorStand1.remove(); + if (armorStand1 != null) { + armorStand1.remove(); + } if (armorStand2 != null) { armorStand2.remove(); } diff --git a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderboardUpdateThread.java b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderboardUpdateThread.java index 49db5af..386ea0a 100644 --- a/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderboardUpdateThread.java +++ b/src/main/java/com/gmail/mezymc/stats/scoreboards/LeaderboardUpdateThread.java @@ -22,8 +22,11 @@ public void run() { } } - // re-run - Bukkit.getScheduler().runTaskLaterAsynchronously(UhcStats.getPlugin(), this, 20*60); + // Re-run if need be + int leaderboardsUpdateInterval = statsManager.getLeaderBoardsUpdateInterval(); + if(leaderboardsUpdateInterval > 0) { + Bukkit.getScheduler().runTaskLaterAsynchronously(UhcStats.getPlugin(), this, 20 * leaderboardsUpdateInterval); + } } public static void runSync(Runnable runnable){ diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b0ae826..0ba453b 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,25 @@ +# Recommended config options + +# For a hub / lobby server in a bungeecord server network: +# use-mysql-database: true +# `gamemodes` section must be populated with corresponding gamemode(s) from your actual UHC game server(s) + +# For a UHC server in a bungeecord server network: +# use-mysql-database: true +# Set `server-gamemode` to type of game being hosted on this UHC server + +# For a standalone UHC server: +# use-mysql-database: false +# Either remove `gamemodes` section or set `server-gamemode` to `uhc` +# Set each leaderboard's `gamemode` to `uhc` +# Remove each leaderboard's `world` section if not using a custom lobby + + +# Database type to use. +# true: use external MySQL database (primarily for server networks) +# false: use local SQLite database file "plugins/UhcStats/database.db" (primarily for standalone servers) +use-mysql-database: true + # MySql details, in this database the stats will be stored. sql: ip: 'localhost' @@ -15,7 +37,8 @@ stats-command: '/stats' gui-title: '&6&lUHC Stats' -# If you have multiple UHC GameModes on your server you can configure them here. If you only have one you can delete this section. +# If you are using a server network (e.g. bungeecord) with multiple UHC servers, you can configure them here. +# If you only have one standalone UHC server you should delete this section. gamemodes: # The current server GameMode. If this is a UHC server under what GameMode should the statistics be saved? server-gamemode: 'cutclean' @@ -30,19 +53,31 @@ gamemodes: name: '&aUhc Run' display-item: 'DIAMOND_PICKAXE' +# How often to update the leader-boards, in seconds +# Set to 0 to disable +leaderboards-update-interval: 60 + +# Set this according to the example configuration section below +# (You can delete this line and adapt the example section to your needs) leaderboards: {} leaderboards-sample: board-1: # Choose from: KILL, DEATH and WIN stat-type: KILL + # If gamemodes section above is not used, set the gamemode below to "uhc". gamemode: cutclean + # Title for this leader-board (can use formatting codes). title: '&aCutClean top 10 kills' - # Layout of the leader-board lines. + # Layout of the leader-board lines (can use formatting codes). lines: '&a%number%. %player%: %count%' # Location where the leader-board should spawn. location: + # Remove the "world: world" line to place this leader-board in the default lobby + # Otherwise, set the world this leader-board should be created in + # If used on a standalone server with the default lobby, this line should be removed world: world + # Coordinates in world to create the leader-board at ([0x 202y 0z] is the center of the default glass box lobby) x: 0 - y: 50 + y: 202 z: 0 \ No newline at end of file