From 8ad2fde798cc02014b0858daf8da32a82704282a Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 13:56:12 +0200 Subject: [PATCH 1/6] Remove REALLY old loading chunk system before teleport --- .../login/ProcessSyncronousPlayerLogin.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java index 20eacdf2e..00322f68d 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java @@ -69,12 +69,8 @@ public class ProcessSyncronousPlayerLogin implements Runnable { protected void teleportBackFromSpawn() { AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, limbo.getLoc()); pm.callEvent(tpEvent); - if (!tpEvent.isCancelled()) { - Location fLoc = tpEvent.getTo(); - if (!fLoc.getChunk().isLoaded()) { - fLoc.getChunk().load(); - } - player.teleport(fLoc); + if (!tpEvent.isCancelled() && tpEvent.getTo() != null) { + player.teleport(tpEvent.getTo()); } } @@ -82,12 +78,8 @@ public class ProcessSyncronousPlayerLogin implements Runnable { Location spawnL = plugin.getSpawnLocation(player); SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnL, true); pm.callEvent(tpEvent); - if (!tpEvent.isCancelled()) { - Location fLoc = tpEvent.getTo(); - if (!fLoc.getChunk().isLoaded()) { - fLoc.getChunk().load(); - } - player.teleport(fLoc); + if (!tpEvent.isCancelled() && tpEvent.getTo() != null) { + player.teleport(tpEvent.getTo()); } } From 3c0e6e06c70dc9af1b5c34c49ba24a4cefc99139 Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 14:57:56 +0200 Subject: [PATCH 2/6] Fix some case when connection is not available --- src/main/java/fr/xephi/authme/AuthMe.java | 10 +- .../fr/xephi/authme/datasource/MySQL.java | 100 +++++++++++++----- 2 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index be4c104b8..98b8f1c25 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -342,11 +342,6 @@ public class AuthMe extends JavaPlugin { } } - // Close the database - if (database != null) { - database.close(); - } - // Do backup on stop if enabled if (Settings.isBackupActivated && Settings.isBackupOnStop) { boolean Backup = new PerformBackup(this).doBackup(); @@ -358,6 +353,11 @@ public class AuthMe extends JavaPlugin { // Unload modules moduleManager.unloadModules(); + // Close the database + if (database != null) { + database.close(); + } + // Disabled correctly ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 8e5d4cc10..30eba2359 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -13,6 +13,8 @@ import java.sql.*; import java.util.ArrayList; import java.util.List; +import org.bukkit.Bukkit; + public class MySQL implements DataSource { private String host; @@ -122,9 +124,24 @@ public class MySQL implements DataSource { } private synchronized Connection getConnection() throws SQLException { - Connection con; - con = ds.getConnection(); - return con; + if (!ds.isClosed()) + { + Connection con; + con = ds.getConnection(); + return con; + } + ConsoleLogger.showError("Can't open a database connection!"); + if (Settings.isStopEnabled) + { + ConsoleLogger.showError("Server will ShuttingDown for Security reason"); + Bukkit.getScheduler().scheduleSyncDelayedTask(AuthMe.getInstance(), new Runnable(){ + @Override + public void run() { + AuthMe.getInstance().getServer().shutdown(); + } + }); + } + return null; } private synchronized void setupConnection() throws SQLException { @@ -132,7 +149,8 @@ public class MySQL implements DataSource { Statement st = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return; st = con.createStatement(); st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + columnID + " INTEGER AUTO_INCREMENT," + columnName + " VARCHAR(255) NOT NULL UNIQUE," + columnPassword + " VARCHAR(255) NOT NULL," + columnIp + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1'," + columnLastLogin + " BIGINT NOT NULL DEFAULT '" + System.currentTimeMillis() + "'," + lastlocX + " DOUBLE NOT NULL DEFAULT '0.0'," + lastlocY + " DOUBLE NOT NULL DEFAULT '0.0'," + lastlocZ + " DOUBLE NOT NULL DEFAULT '0.0'," + lastlocWorld + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," + columnEmail + " VARCHAR(255) DEFAULT 'your@email.com'," + columnLogged + " SMALLINT NOT NULL DEFAULT '0'," + "CONSTRAINT table_const_prim PRIMARY KEY (" + columnID + "));"); rs = con.getMetaData().getColumns(null, null, tableName, columnPassword); @@ -193,7 +211,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return true; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + columnName + ")=LOWER(?);"); pst.setString(1, user); rs = pst.executeQuery(); @@ -216,7 +235,8 @@ public class MySQL implements DataSource { PlayerAuth pAuth = null; int id; try { - con = getConnection(); + if ((con = getConnection()) == null) + return null; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + columnName + ")=LOWER(?);"); pst.setString(1, user); rs = pst.executeQuery(); @@ -266,7 +286,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return false; if ((columnSalt == null || columnSalt.isEmpty()) || (auth.getSalt() == null || auth.getSalt().isEmpty())) { pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + "," + columnIp + "," + columnLastLogin + "," + columnRealName + ") VALUES (?,?,?,?,?);"); pst.setString(1, auth.getNickname()); @@ -472,7 +493,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return false; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnPassword + "=? WHERE LOWER(" + columnName + ")=?;"); pst.setString(1, auth.getHash()); pst.setString(2, auth.getNickname()); @@ -515,7 +537,8 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return false; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnIp + "=?, " + columnLastLogin + "=?, " + columnRealName + "=? WHERE LOWER(" + columnName + ")=?;"); pst.setString(1, auth.getIp()); pst.setLong(2, auth.getLastLogin()); @@ -537,7 +560,8 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return 0; pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + columnLastLogin + " list = new ArrayList<>(); try { - con = getConnection(); + if ((con = getConnection()) == null) + return list; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnLastLogin + " countIp = new ArrayList<>(); try { - con = getConnection(); + if ((con = getConnection()) == null) + return countIp; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnIp + "=?;"); pst.setString(1, auth.getIp()); rs = pst.executeQuery(); @@ -769,7 +800,8 @@ public class MySQL implements DataSource { ResultSet rs = null; List countIp = new ArrayList<>(); try { - con = getConnection(); + if ((con = getConnection()) == null) + return countIp; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnIp + "=?;"); pst.setString(1, ip); rs = pst.executeQuery(); @@ -794,7 +826,8 @@ public class MySQL implements DataSource { ResultSet rs = null; List countEmail = new ArrayList<>(); try { - con = getConnection(); + if ((con = getConnection()) == null) + return countEmail; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnEmail + "=?;"); pst.setString(1, email); rs = pst.executeQuery(); @@ -817,8 +850,9 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { + if ((con = getConnection()) == null) + return; for (String name : banned) { - con = getConnection(); pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE LOWER(" + columnName + ")=?;"); pst.setString(1, name); pst.executeUpdate(); @@ -842,7 +876,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return false; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + columnName + ")=?;"); pst.setString(1, user); rs = pst.executeQuery(); @@ -864,7 +899,8 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnLogged + "=? WHERE LOWER(" + columnName + ")=?;"); pst.setInt(1, 1); pst.setString(2, user); @@ -883,7 +919,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; if (user != null) try { - con = getConnection(); + if ((con = getConnection()) == null) + return; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnLogged + "=? WHERE LOWER(" + columnName + ")=?;"); pst.setInt(1, 0); pst.setString(2, user); @@ -901,7 +938,8 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnLogged + "=? WHERE " + columnLogged + "=?;"); pst.setInt(1, 0); pst.setInt(2, 1); @@ -921,7 +959,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs; try { - con = getConnection(); + if ((con = getConnection()) == null) + return result; pst = con.prepareStatement("SELECT COUNT(*) FROM " + tableName + ";"); rs = pst.executeQuery(); if (rs != null && rs.next()) { @@ -942,7 +981,8 @@ public class MySQL implements DataSource { Connection con = null; PreparedStatement pst = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return; pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnName + "=? WHERE LOWER(" + columnName + ")=?;"); pst.setString(1, newone); pst.setString(2, oldone); @@ -962,7 +1002,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return auths; pst = con.prepareStatement("SELECT * FROM " + tableName + ";"); rs = pst.executeQuery(); while (rs.next()) { @@ -1012,7 +1053,8 @@ public class MySQL implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - con = getConnection(); + if ((con = getConnection()) == null) + return auths; pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnLogged + "=1;"); rs = pst.executeQuery(); while (rs.next()) { From 5b98759b61611106b379ead6fb5f1de2287ff14a Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 15:01:57 +0200 Subject: [PATCH 3/6] Change how purge logged works at starting/stopping server --- src/main/java/fr/xephi/authme/AuthMe.java | 25 ++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 98b8f1c25..6596ae53b 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -263,19 +263,17 @@ public class AuthMe extends JavaPlugin { } // Reload support hook - if (Settings.reloadSupport) { - if (database != null) { - int playersOnline = Utils.getOnlinePlayers().size(); - if (playersOnline < 1) { - database.purgeLogged(); - } else { - for (PlayerAuth auth : database.getLoggedPlayers()) { - if (auth == null) - continue; - auth.setLastLogin(new Date().getTime()); - database.updateSession(auth); - PlayerCache.getInstance().addPlayer(auth); - } + if (database != null) { + int playersOnline = Utils.getOnlinePlayers().size(); + if (playersOnline < 1) { + database.purgeLogged(); + } else if (Settings.reloadSupport) { + for (PlayerAuth auth : database.getLoggedPlayers()) { + if (auth == null) + continue; + auth.setLastLogin(new Date().getTime()); + database.updateSession(auth); + PlayerCache.getInstance().addPlayer(auth); } } } @@ -564,7 +562,6 @@ public class AuthMe extends JavaPlugin { } } PlayerCache.getInstance().removePlayer(name); - database.setUnlogged(name); player.saveData(); } From 8e90a5f9a82aba9622bf81420394b8fbc767a976 Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 15:10:14 +0200 Subject: [PATCH 4/6] Force a player just registered by an admin to log again --- src/main/java/fr/xephi/authme/commands/AdminCommand.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/fr/xephi/authme/commands/AdminCommand.java b/src/main/java/fr/xephi/authme/commands/AdminCommand.java index 49042a433..a393a72e2 100644 --- a/src/main/java/fr/xephi/authme/commands/AdminCommand.java +++ b/src/main/java/fr/xephi/authme/commands/AdminCommand.java @@ -233,6 +233,7 @@ public class AdminCommand implements CommandExecutor { return true; } final String name = args[1].toLowerCase(); + final String realName = args[1]; final String lowpass = args[2].toLowerCase(); if (lowpass.contains("delete") || lowpass.contains("where") || lowpass.contains("insert") || lowpass.contains("modify") || lowpass.contains("from") || lowpass.contains("select") || lowpass.contains(";") || lowpass.contains("null") || !lowpass.matches(Settings.getPassRegex)) { m.send(sender, "password_error"); @@ -269,6 +270,9 @@ public class AdminCommand implements CommandExecutor { m.send(sender, "error"); return; } + plugin.database.setUnlogged(name); + if (Bukkit.getPlayerExact(realName) != null) + Bukkit.getPlayerExact(realName).kickPlayer("An admin just registered you, please log again"); m.send(sender, "registered"); ConsoleLogger.info(name + " registered"); } catch (NoSuchAlgorithmException ex) { From 5e991f7f420bc53244e1588a5d6eeb91f4b926c3 Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 15:12:01 +0200 Subject: [PATCH 5/6] realName here --- src/main/java/fr/xephi/authme/commands/AdminCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/xephi/authme/commands/AdminCommand.java b/src/main/java/fr/xephi/authme/commands/AdminCommand.java index a393a72e2..52265467f 100644 --- a/src/main/java/fr/xephi/authme/commands/AdminCommand.java +++ b/src/main/java/fr/xephi/authme/commands/AdminCommand.java @@ -262,7 +262,7 @@ public class AdminCommand implements CommandExecutor { return; } String hash = PasswordSecurity.getHash(Settings.getPasswordHash, lowpass, name); - PlayerAuth auth = new PlayerAuth(name, hash, "192.168.0.1", 0L, "your@email.com", name); + PlayerAuth auth = new PlayerAuth(name, hash, "192.168.0.1", 0L, "your@email.com", realName); if (PasswordSecurity.userSalt.containsKey(name) && PasswordSecurity.userSalt.get(name) != null) auth.setSalt(PasswordSecurity.userSalt.get(name)); else auth.setSalt(""); From 39ab41f542b87a749a71556a9fbddb52a7d90bce Mon Sep 17 00:00:00 2001 From: vanhec_a Date: Mon, 12 Oct 2015 15:49:19 +0200 Subject: [PATCH 6/6] Add an option to disable all caching (useful if you use website registration system) --- .../fr/xephi/authme/datasource/MySQL.java | 14 +++++++-- .../fr/xephi/authme/settings/Settings.java | 7 ++++- src/main/resources/config.yml | 30 ++++++++++--------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 30eba2359..82423bd4b 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -102,9 +102,15 @@ public class MySQL implements DataSource { config.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); config.setUsername(this.username); config.setPassword(this.password); - config.addDataSourceProperty("cachePrepStmts", "true"); - config.addDataSourceProperty("prepStmtCacheSize", "250"); - config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + if (Settings.isMySQLWebsite) + { + config.addDataSourceProperty("cachePrepStmts", "false"); + } + else { + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + } config.addDataSourceProperty("autoReconnect", false); config.setInitializationFailFast(true); // Don't start the plugin if the database is unavariable config.setMaxLifetime(180000); // 3 Min @@ -197,6 +203,8 @@ public class MySQL implements DataSource { if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnRealName + " VARCHAR(255) NOT NULL DEFAULT 'Player' AFTER " + columnLogged + ";"); } + if (Settings.isMySQLWebsite) + st.execute("SET GLOBAL query_cache_size = 0; SET GLOBAL query_cache_type = 0;"); } finally { close(rs); close(st); diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 9264ae54a..d185a4c1c 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -68,7 +68,7 @@ public final class Settings extends YamlConfiguration { enableProtection, enableAntiBot, recallEmail, useWelcomeMessage, broadcastWelcomeMessage, forceRegKick, forceRegLogin, checkVeryGames, delayJoinMessage, noTeleport, applyBlindEffect, - customAttributes, generateImage, isRemoveSpeedEnabled; + customAttributes, generateImage, isRemoveSpeedEnabled, isMySQLWebsite; public static String getNickRegex, getUnloggedinGroup, getMySQLHost, getMySQLPort, getMySQLUsername, getMySQLPassword, getMySQLDatabase, @@ -275,6 +275,7 @@ public final class Settings extends YamlConfiguration { forceRegisterCommandsAsConsole = configFile.getStringList("settings.forceRegisterCommandsAsConsole"); customAttributes = configFile.getBoolean("Hooks.customAttributes"); generateImage = configFile.getBoolean("Email.generateImage", true); + isMySQLWebsite = configFile.getBoolean("DataSource.mySQLWebsite", false); // Load the welcome message getWelcomeMessage(); @@ -476,6 +477,10 @@ public final class Settings extends YamlConfiguration { set("DataSource.mySQLRealName", "realname"); changes = true; } + if (!contains("DataSource.mySQLQueryCache")) { + set("DataSource.mySQLWebsite", false); + changes = true; + } if (changes) { plugin.getLogger().warning("Merged new Config Options - I'm not an error, please don't report me"); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c10c977e7..ef9fd2144 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -43,12 +43,14 @@ DataSource: mySQLlastlocWorld: world # Column for RealName mySQLRealName: realname + # Enable this when you allow registration through a website + mySQLWebsite: false settings: sessions: - # Do you want to enable the session feature? + # Do you want to enable the session feature? # If enabled, when a player authenticates successfully, # his IP and his nickname is saved. - # The next time the player joins the server, if his IP + # The next time the player joins the server, if his IP # is the same of the last time, and the timeout time # hasn't expired, he will not need to authenticate. enabled: false @@ -62,7 +64,7 @@ settings: # another IP Address? sessionExpireOnIpChange: true restrictions: - # Can not authenticated players chat and see the chat log? + # Can not authenticated players chat and see the chat log? # Care that this feature blocks also all the commands not # listed in the list below. allowChat: false @@ -109,7 +111,7 @@ settings: # After the authentication they will be teleported back to # their normal position. teleportUnAuthedToSpawn: false - # Minimum allowed nick length + # Minimum allowed nick length minNicknameLength: 4 # Can unregistered players walk around? allowMovement: false @@ -161,25 +163,25 @@ settings: security: # minimum Length of password minPasswordLength: 5 - # this is very important options, + # this is very important options, # every time player join the server, - # if they are registered, AuthMe will switch him + # if they are registered, AuthMe will switch him # to unLoggedInGroup, this - # should prevent all major exploit. + # should prevent all major exploit. # So you can set up on your Permission Plugin - # this special group with 0 permissions, or permissions to chat, + # this special group with 0 permissions, or permissions to chat, # or permission to - # send private message or all other perms that you want, + # send private message or all other perms that you want, # the better way is to set up # this group with few permissions, # so if player try to exploit some account, # they can # do anything except what you set in perm Group. - # After a correct logged-in player will be + # After a correct logged-in player will be # moved to his correct permissions group! - # Pay attention group name is case sensitive, - # so Admin is different from admin, - # otherwise your group will be wiped, + # Pay attention group name is case sensitive, + # so Admin is different from admin, + # otherwise your group will be wiped, # and player join in default group []! # Example unLoggedinGroup: NotLogged unLoggedinGroup: unLoggedinGroup @@ -244,7 +246,7 @@ settings: # Force these commands after /register as a server console, without any '/', use %p for replace with player name forceRegisterCommandsAsConsole: [] # Do we need to display the welcome message (welcome.txt) after a register or a login? - # You can use colors in this welcome.txt + some replaced strings : + # You can use colors in this welcome.txt + some replaced strings : # {PLAYER} : player name, {ONLINE} : display number of online players, {MAXPLAYERS} : display server slots, # {IP} : player ip, {LOGINS} : number of players logged, {WORLD} : player current world, {SERVER} : server name # {VERSION} : get current bukkit version, {COUNTRY} : player country