/* * This file is part of Libelula Minecraft Edition Project. * * Libelula Minecraft Edition is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Libelula Minecraft Edition is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Libelula Minecraft Edition. * If not, see <http://www.gnu.org/licenses/>. * */ package me.libelula.lobby; import java.net.InetAddress; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TreeMap; import java.util.concurrent.locks.ReentrantLock; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.permissions.PermissionAttachment; /** * * @author Diego D'Onofrio <ddonofrio@member.fsf.org> <ddonofrio@member.fsf.org> */ public class PlayerManager { public enum playerState { ERROR, PROCESSING, BANNED, UNREGISTERED, REGISTERED, LOGGED_IN, PREMIUM, BOGGED_DOWN, UNACTIVATED } public enum nonPremiumChecks { TARPPITING, CHECKING_FOR_REGISTERED, CHECKING_FOR_PREMIUM, LOGGING_IN, SETTING_PREMIUM, SENDING_TO_LOBBY } protected class PlayerInstance { private Player player; private playerState state; private nonPremiumChecks checkStatus; private long stateDateTime; private InetAddress lastInetAddress; private String typedPassword; private final String lowerCasedName; private long lastTalked; private String resourcePackName; private ArrayList<ItemStack> armour; public PlayerInstance(Player player) { this.player = player; this.lowerCasedName = player.getName().toLowerCase(); this.lastInetAddress = player.getAddress().getAddress(); lastTalked = -1; ItemStack resourcePackIs = getResourcePackItemFromInv(player); if (resourcePackIs != null) { setResourcePack( ChatColor.stripColor(resourcePackIs.getItemMeta().getDisplayName())); } } public void setArmour(ArrayList<ItemStack> armour) { this.armour = armour; } public ArrayList<ItemStack> getArmour() { return armour; } public final void setResourcePack(String resourcePackName) { this.resourcePackName = resourcePackName; String packLink = null; switch (resourcePackName) { case "R3D Craft": packLink = "http://libelula.me/resourcepacks/R3D.CRAFT_SR-64x_v0.2.0.zip"; break; case "Traditional Beauty": packLink = "http://libelula.me/resourcepacks/Traditional_Beauty_18v2.zip"; break; case "Pure BD Craft": packLink = "http://libelula.me/resourcepacks/PureBDcraft_64x_MC18.zip"; break; case "Soartex Fanver (Light)": packLink = "http://libelula.me/resourcepacks/Soartex_Fanver_Lite.zip"; break; case "Modern HD": packLink = "http://libelula.me/resourcepacks/ModernHD_1.8.1.zip"; break; case "Soartex Invictus": packLink = "http://libelula.me/resourcepacks/Soartex_Invictus-2.1.zip"; break; } if (packLink != null) { if (cm.isDebugMode()) { plugin.logInfo("Sending resource pack: '" + packLink + "' to " + player.getName()); } player.setResourcePack(packLink); plugin.sendMessage(player, "Te hemos enviado las texturas, si no se han cargado es porque tienes desactivadas las texturas de servidor en tu minecraft.", 40); plugin.sendMessage(player, "Puedes descargar este resource pack desde &1&n" + packLink, 42); } } public String getResourcePackName() { return resourcePackName; } public final ItemStack getResourcePackItemFromInv(Player player) { ItemStack is = null; if (player.getInventory().getItem(8) != null) { String playerItemName = ChatColor.stripColor(player.getInventory().getItem(8).getItemMeta().getDisplayName()); for (ItemStack rsIs : resourcePacks) { if (ChatColor.stripColor(rsIs.getItemMeta().getDisplayName()) .equals(playerItemName)) { is = rsIs; break; } } } return is; } public final ItemStack getSelectedResourceItem() { ItemStack is; is = new ItemStack(Material.SPONGE); ItemMeta im = is.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Selector de Resource packs"); is.setItemMeta(im); if (player.getInventory().getItem(8) != null) { is = getResourcePackItemFromInv(player); } else { if (resourcePackName != null) { for (ItemStack rsIs : resourcePacks) { if (ChatColor.stripColor(rsIs.getItemMeta().getDisplayName()).equals(resourcePackName)) { is = rsIs; break; } } } } return is; } public void sendMessage(final String text) { Bukkit.getScheduler().runTask(plugin, () -> { plugin.sendMessage(player, text); }); } public void setLastInetAddress(InetAddress lastInetAddress) { this.lastInetAddress = lastInetAddress; } public void kickPlayer(final String text) { plugin.pm.kickPlayer(player, text); } public Player getPlayer() { return player; } public playerState getState() { return state; } public long getStateDateTime() { return stateDateTime; } public void setState(playerState state) { this.state = state; } public void setStateDateTime(long stateDateTime) { this.stateDateTime = stateDateTime; } public InetAddress getLastInetAddress() { return lastInetAddress; } public String getTypedPassword() { return typedPassword; } public void setCheckStatus(nonPremiumChecks checkStatus) { this.state = playerState.PROCESSING; this.checkStatus = checkStatus; _players_mutex.lock(); try { processQueue.remove(lowerCasedName); processQueue.add(lowerCasedName); stateDateTime = new Date().getTime(); } finally { _players_mutex.unlock(); } } } private final Main plugin; private final TreeMap<String, PlayerInstance> players; private final ArrayList<String> processQueue; private final ReentrantLock _players_mutex; private final ConfigurationManager cm; private boolean processing; private final ArrayList<Location> spawns; private int lastSpawnPoint; private final ItemStack itemStackAir; private final ItemStack beginnersGuide; private final ItemStack rulesBook; private final ItemStack helpBook; private final ItemStack compass; private final ArrayList<ItemStack> resourcePacks; public PlayerManager(Main plugin) { this.plugin = plugin; players = new TreeMap<>(); processQueue = new ArrayList<>(); _players_mutex = new ReentrantLock(); this.cm = plugin.cm; processing = false; spawns = plugin.cm.getSpawnPoints(); lastSpawnPoint = 0; this.itemStackAir = new ItemStack(Material.AIR); this.beginnersGuide = getBook("beginners"); this.rulesBook = getBook("rules"); this.helpBook = getBook("help"); this.compass = getCompass(); resourcePacks = new ArrayList<>(); ItemStack resourcePackItem; ItemMeta im; ArrayList<String> lore = new ArrayList<>(); lore.add("Click para activar"); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "R3D Craft"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Traditional Beauty"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Pure BD Craft"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Soartex Fanver (Light)"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Modern HD"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); resourcePackItem = new ItemStack(Material.SPONGE); im = resourcePackItem.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Soartex Invictus"); im.setLore(lore); resourcePackItem.setItemMeta(im); resourcePacks.add(resourcePackItem); Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> { processQueue(); }, 10, 10); if (plugin.cm.isFrontServer()) { Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> { checkNonPremiumPlayerList(); }, 15, 15); } } public void kickPlayer(final Player player, final String text) { kickPlayer(player, text, 1); } public void kickPlayer(Player givenPlayer, final String text, final int tics) { final Player player = plugin.getServer().getPlayer(givenPlayer.getUniqueId()); Bukkit.getScheduler().runTaskLater(plugin, () -> { if (tics != 0) { if (player == null || !player.isOnline()) { return; } } if (cm.isDebugMode()) { plugin.logInfo("Debug: Kick " + player.getName() + " \"" + text + "\""); } player.kickPlayer(text); }, tics); } public void registerUser(Player player) { PlayerInstance pi; _players_mutex.lock(); try { pi = players.get(player.getName().toLowerCase()); } finally { _players_mutex.unlock(); } if (pi != null) { pi.player = player; } if (cm.isPremium()) { if (pi == null) { pi = new PlayerInstance(player); pi.setCheckStatus(nonPremiumChecks.SETTING_PREMIUM); addPlayerInstance(pi); } return; } if (pi != null) { switch (pi.getState()) { case BOGGED_DOWN: long now = new Date().getTime(); if (now - pi.getStateDateTime() < cm.getTarpittingPenaltySeconds() * 1000) { plugin.pm.kickPlayer(player, cm.getTarpittingPenaltyMessage(), 30); } else { pi.setCheckStatus(nonPremiumChecks.TARPPITING); } break; case BANNED: pi.kickPlayer("Tu cuenta está desactivada, si crees que es un error escribe a info@libelula.me"); plugin.logInfo(pi.getPlayer().getName() + " echado por estar baneado en el foro."); break; case PREMIUM: if (!cm.isAllowPremiumOnNonpremium() && !cm.isPremium()) { pi.kickPlayer(cm.getPremiumKickMessage()); plugin.logInfo(pi.getPlayer().getName() + " echado por ser premium."); } break; case LOGGED_IN: if (pi.getLastInetAddress().equals(player.getAddress().getAddress())) { pi.sendMessage("&1[&6Autologin&1]&6 ¡Bienvenido de regreso " + player.getName() + "!"); plugin.logInfo(pi.getPlayer().getName() + " Auto logged in."); } else { // Different IP pi.state = playerState.REGISTERED; pi.sendMessage("Hola " + player.getName() + ", escribe /login y tu contraseña para empezar."); } break; case PROCESSING: plugin.pm.kickPlayer(player, "Ahora no puedes entrar, aguarda unos momentos y vuelve a intentarlo.", 30); plugin.logWarn("Echado el jugador " + pi.getPlayer().getName() + " por entrar mientras se procesan sus datos."); break; case REGISTERED: if (cm.isFrontServer()) { pi.setCheckStatus(nonPremiumChecks.TARPPITING); } break; default: plugin.logErr("Estado de jugador no válido: " + pi.getState() + " para " + player.getName()); removePlayerFromList(player); pi = null; } } if (pi == null) { pi = new PlayerInstance(player); if (cm.isFrontServer()) { pi.setCheckStatus(nonPremiumChecks.TARPPITING); } else { pi.state = playerState.REGISTERED; } addPlayerInstance(pi); } if (!cm.isFrontServer()) { switch (pi.getState()) { case REGISTERED: giveUnloggedStuff(player); break; case LOGGED_IN: case PREMIUM: giveLoggedStuff(player); break; } } } private void addPlayerInstance(PlayerInstance pi) { _players_mutex.lock(); try { players.put(pi.lowerCasedName, pi); } finally { _players_mutex.unlock(); } } public void removePlayerFromList(Player player) { _players_mutex.lock(); try { PlayerInstance pi = players.remove(player.getName().toLowerCase()); if (pi != null && pi.getState() != null && pi.getState() == playerState.PROCESSING) { processQueue.remove(pi.lowerCasedName); } } finally { _players_mutex.unlock(); } } private void processQueue() { boolean exit = false; _players_mutex.lock(); try { if (!processing) { processing = true; } else { exit = true; } } finally { _players_mutex.unlock(); } if (exit) { return; } PlayerInstance pi; int loops = 0; while (!processQueue.isEmpty()) { _players_mutex.lock(); try { pi = players.get(processQueue.remove(0)); } finally { _players_mutex.unlock(); } if (pi != null && pi.state == playerState.PROCESSING && pi.checkStatus != null) { switch (pi.checkStatus) { case TARPPITING: long time = (new Date().getTime()) - pi.stateDateTime; if (time < cm.getTarpittingMs()) { _players_mutex.lock(); try { processQueue.add(pi.lowerCasedName); } finally { _players_mutex.unlock(); } } else { if (pi.player.isOnline()) { pi.setCheckStatus(nonPremiumChecks.CHECKING_FOR_REGISTERED); } else { if (cm.isDebugMode()) { plugin.logInfo("player " + pi.getPlayer().getName() + " has been bogged down."); } pi.setCheckStatus(nonPremiumChecks.TARPPITING); pi.setState(playerState.BOGGED_DOWN); } } break; case CHECKING_FOR_REGISTERED: plugin.xr.checkForRegistered(pi); break; case CHECKING_FOR_PREMIUM: plugin.xr.checkForPremium(pi); break; case LOGGING_IN: plugin.xr.checkPassword(pi); break; case SETTING_PREMIUM: plugin.xr.setPremium(pi); pi.setState(playerState.PREMIUM); giveLoggedStuff(pi.getPlayer()); break; case SENDING_TO_LOBBY: plugin.teleportToServer(pi.getPlayer(), cm.getLobbyServerName()); removePlayerFromList(pi.player); break; default: plugin.logErr("Estado no válido en check status: " + pi.checkStatus); break; } } if (processQueue.size() < loops) { break; } loops++; } _players_mutex.lock(); try { processing = false; } finally { _players_mutex.unlock(); } } public void spawn(Player player) { Bukkit.getScheduler().runTask(plugin, () -> { player.teleport(spawns.get(lastSpawnPoint)); }); lastSpawnPoint++; if (lastSpawnPoint >= spawns.size()) { lastSpawnPoint = 0; } } private void checkNonPremiumPlayerList() { for (Player player : plugin.getServer().getOnlinePlayers()) { PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { switch (pi.state) { case PREMIUM: pi.kickPlayer(cm.getPremiumKickMessage()); break; case BANNED: pi.kickPlayer("Tu cuenta está desactivada, si crees que es un error escribe a info@libelula.me"); break; case BOGGED_DOWN: long now = new Date().getTime(); if (now - pi.getStateDateTime() < cm.getTarpittingPenaltySeconds() * 1000) { plugin.pm.kickPlayer(player, cm.getTarpittingPenaltyMessage(), 30); } else { pi.setCheckStatus(nonPremiumChecks.TARPPITING); } break; // case REGISTERED: // pi.kickPlayer("Error yendo al servidor esperado."); } } } } public boolean isPremium(Player player) { boolean result = false; PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { if (pi.state == playerState.PREMIUM) { result = true; } } return result; } public boolean isBoggedDown(Player player) { boolean result = false; PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { if (pi.state == playerState.BOGGED_DOWN) { long now = new Date().getTime(); if (now - pi.getStateDateTime() < cm.getTarpittingPenaltySeconds() * 1000) { result = true; } } } return result; } public boolean isLogged(Player player) { boolean result = false; PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { result = (pi.state == playerState.LOGGED_IN || pi.state == playerState.PREMIUM); } return result; } public void logginSucces(PlayerInstance pi) { pi.setState(playerState.LOGGED_IN); plugin.sendMessage(pi.getPlayer(), "* Bienvenido a Libélula Minecraft Edition *"); giveLoggedStuff(pi.getPlayer()); } public void giveUnloggedStuff(Player player) { clearStuff(player); player.getInventory().addItem(beginnersGuide); spawn(player); PermissionAttachment attachment = player.addAttachment(plugin); attachment.setPermission("lobby.unlogged", true); attachment.setPermission("lobby.logged", false); } public void giveLoggedStuff(Player player) { boolean canFly = player.getLocation().distance(plugin.cm.getZeroPoint()) < plugin.cm.getPlayerFlyBlocksFromZero(); if (canFly) { player.teleport(player.getLocation().add(0, 1, 0)); } clearStuff(player, canFly); player.getInventory().addItem(compass); if (!cm.isAltmenu()) { player.getInventory().setItem(6, helpBook); player.getInventory().setItem(7, rulesBook); PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { player.getInventory().setItem(8, pi.getSelectedResourceItem()); int inverntorySlot = 27; for (ItemStack rsIs : resourcePacks) { if (rsIs.getItemMeta().getDisplayName() .equals(player.getInventory().getItem(8).getItemMeta().getDisplayName())) { continue; } player.getInventory().setItem(inverntorySlot++, rsIs); } } } player.getInventory().setHeldItemSlot(0); PermissionAttachment attachment = player.addAttachment(plugin); attachment.setPermission("lobby.unlogged", false); attachment.setPermission("lobby.logged", true); } public void clearStuff(Player player) { clearStuff(player, false); } public void clearStuff(Player player, boolean allowFlight) { if (!cm.isOpAllowed(player)) { player.setOp(false); } player.setAllowFlight(allowFlight); player.setFlying(allowFlight); player.setCompassTarget(cm.getZeroPoint()); player.setFoodLevel(20); player.setHealth(20); player.setGameMode(GameMode.ADVENTURE); player.setLevel(0); player.setExp(0); player.getInventory().clear(); /* player.getInventory().setBoots(itemStackAir); player.getInventory().setChestplate(itemStackAir); player.getInventory().setHelmet(itemStackAir); player.getInventory().setLeggings(itemStackAir); */ } public final ItemStack getBook(String bookName) { List<String> bookPages = cm.getBookPages(bookName); ItemStack book = new ItemStack(Material.WRITTEN_BOOK, 1); BookMeta bm = (BookMeta) book.getItemMeta(); bm.setDisplayName(ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString(bookName + "-book.title"))); bm.setAuthor(ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString(bookName + "-book.author"))); bm.setTitle(ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString(bookName + "-book.title"))); bm.setPages(bookPages); bm.addEnchant(Enchantment.LUCK, 1, true); book.setItemMeta(bm); return book; } public final ItemStack getCompass() { ItemStack is = new ItemStack(Material.COMPASS); ItemMeta im = is.getItemMeta(); im.setDisplayName(ChatColor.YELLOW + "Navegador" + ChatColor.GOLD + " Libelula"); is.setItemMeta(im); return is; } public boolean requestLogIn(Player player, String typedPassword) { boolean result = false; PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { switch (pi.state) { case PROCESSING: pi.sendMessage("Espera unos momentos y vuelve a intentarlo..."); break; case BANNED: player.kickPlayer("Tu cuenta ha sido desactivada por un administrador."); break; case UNACTIVATED: player.kickPlayer("Regresa al menos 24 horas después de haberte registrado."); break; default: result = true; pi.typedPassword = typedPassword; pi.setCheckStatus(nonPremiumChecks.LOGGING_IN); } } return result; } public int getMsBetweenLastTlkCmd(Player player) { int result = Integer.MAX_VALUE; PlayerInstance pi = players.get(player.getName().toLowerCase()); if (pi != null) { long now = new Date().getTime(); if (pi.lastTalked != -1) { result = (int) (now - pi.lastTalked); } pi.lastTalked = now; } return result; } public void showServerMenu(Player player) { } public void showTextureMenu(Player player) { } public void manegeSpecialClick(Player player, ItemStack is) { PlayerInstance pi = players.get(player.getName().toLowerCase()); String playerItemName = ChatColor.stripColor(is.getItemMeta().getDisplayName()); boolean found = false; for (ItemStack rsIs : resourcePacks) { if (ChatColor.stripColor(rsIs.getItemMeta().getDisplayName()) .equals(playerItemName)) { pi.setResourcePack(playerItemName); found = true; break; } } if (found) { player.getInventory().setItem(8, is); } } }