From a37d8ecb318f4988dd2f165b29d4e98d03d425e8 Mon Sep 17 00:00:00 2001 From: ZeroEpoch1969 <13510767+ZeroEpoch1969@users.noreply.github.com> Date: Fri, 24 Jan 2020 23:27:16 -0700 Subject: [PATCH] yeah --- TotalFreedomMod.iml | 4 +- pom.xml | 19 ++ .../totalfreedommod/EntityWiper.java | 55 +++- .../totalfreedommod/LoginProcess.java | 18 ++ .../totalfreedommod/admin/Admin.java | 24 +- .../command/Command_chatcolor.java | 53 ---- .../command/Command_cleardiscordqueue.java | 20 ++ .../command/Command_entitywipe.java | 94 ++++++- .../command/Command_linkdiscord.java | 50 ++-- .../command/Command_mobpurge.java | 36 +-- .../command/Command_myadmin.java | 100 ++++++- .../command/Command_mymasterbuilder.java | 237 +++++++++++++++++ .../command/Command_notes.java | 135 ++++++++++ .../command/Command_playerverify.java | 46 +++- .../command/Command_purgeall.java | 2 +- .../command/Command_realtime.java | 4 +- .../command/Command_verify.java | 142 ++++++++-- .../totalfreedommod/discord/Discord.java | 248 +++++++++++++++++- .../discord/PrivateMessageListener.java | 33 ++- .../masterbuilder/MasterBuilder.java | 24 +- .../PlayerVerification.java | 6 +- .../playerverification/VPlayer.java | 67 ++++- .../totalfreedommod/util/FUtil.java | 15 ++ 23 files changed, 1243 insertions(+), 189 deletions(-) delete mode 100644 src/main/java/me/totalfreedom/totalfreedommod/command/Command_chatcolor.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/command/Command_mymasterbuilder.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java diff --git a/TotalFreedomMod.iml b/TotalFreedomMod.iml index 42591655..a17c989b 100644 --- a/TotalFreedomMod.iml +++ b/TotalFreedomMod.iml @@ -49,7 +49,7 @@ - + @@ -82,7 +82,6 @@ - @@ -163,5 +162,6 @@ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1b8638cf..788a6761 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,11 @@ papermc https://papermc.io/repo/repository/maven-public/ + + + rayzr-repo + https://cdn.rawgit.com/Rayzr522/maven-repo/master/ + @@ -118,6 +123,13 @@ compile + + commons-codec + commons-codec + 1.9 + compile + + org.spigotmc spigot @@ -187,6 +199,12 @@ 1.15.1-R0.1-SNAPSHOT provided + + + me.rayzr522 + jsonmessage + 1.0.0 + @@ -344,6 +362,7 @@ commons-io:commons-io org.apache.commons:commons-lang3 + commons-codec:commons-codec diff --git a/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java b/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java index 1b89127a..030b9ac0 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java @@ -7,6 +7,8 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @@ -23,7 +25,6 @@ public class EntityWiper extends FreedomService EntityType.ARMOR_STAND, EntityType.PAINTING, EntityType.BOAT, - EntityType.PLAYER, EntityType.LEASH_HITCH, EntityType.ITEM_FRAME ); @@ -37,9 +38,9 @@ public class EntityWiper extends FreedomService @Override public void run() { - wipe(); + wipeEntities(false); } - }.runTaskTimer(plugin, 1L, 300 * 5); // 5 minutes + }.runTaskTimer(plugin, 600L, 600L); // 30 second delay after startup + run every 30 seconds } @Override @@ -51,15 +52,19 @@ public class EntityWiper extends FreedomService // Methods for wiping - public int wipe() + public int wipeEntities(boolean bypassBlacklist) { int removed = 0; for (World world : Bukkit.getWorlds()) { for (Entity entity : world.getEntities()) { - if (!BLACKLIST.contains(entity.getType()) || !Groups.MOB_TYPES.contains(entity.getType())) + if (!(entity instanceof Player)) { + if (!bypassBlacklist && (BLACKLIST.contains(entity.getType()) || Groups.MOB_TYPES.contains(entity.getType()))) + { + continue; + } entity.remove(); removed++; } @@ -67,4 +72,44 @@ public class EntityWiper extends FreedomService } return removed; } + + public int wipeEntities(EntityType entityType) + { + int removed = 0; + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getEntities()) + { + if (!entity.getType().equals(entityType)) + { + continue; + } + entity.remove(); + removed++; + } + } + return removed; + } + + public int purgeMobs(EntityType type) + { + int removed = 0; + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getLivingEntities()) + { + if (entity instanceof LivingEntity && !(entity instanceof Player)) + { + if (type != null && !entity.getType().equals(type)) + { + continue; + } + entity.remove(); + removed++; + } + } + } + return removed; + } + } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java b/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java index 00da1f8c..d98b7456 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java @@ -8,6 +8,7 @@ import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; import me.totalfreedom.totalfreedommod.player.FPlayer; import me.totalfreedom.totalfreedommod.playerverification.VPlayer; +import me.totalfreedom.totalfreedommod.util.FLog; import me.totalfreedom.totalfreedommod.util.FSync; import me.totalfreedom.totalfreedommod.util.FUtil; import org.bukkit.ChatColor; @@ -19,6 +20,7 @@ import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.scheduler.BukkitRunnable; +import me.rayzr522.jsonmessage.JSONMessage; public class LoginProcess extends FreedomService { @@ -246,6 +248,22 @@ public class LoginProcess extends FreedomService { fPlayer.setTag(FUtil.colorize(vPlayer.getTag())); } + int noteCount = vPlayer.getNotes().size(); + if (noteCount != 0) + { + String noteMessage = "This player has " + noteCount + " staff note" + (noteCount > 1 ? "s" : "") + "."; + JSONMessage notice = JSONMessage.create(ChatColor.GOLD + noteMessage + " Click here to view them.") + .tooltip("Click here to view them.") + .runCommand("/notes " + player.getName() + " list"); + FLog.info(noteMessage); + for (Player p : server.getOnlinePlayers()) + { + if (plugin.al.isAdminImpostor(p)) + { + notice.send(p); + } + } + } } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java b/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java index a921165f..385422f2 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java @@ -1,6 +1,7 @@ package me.totalfreedom.totalfreedommod.admin; import com.google.common.collect.Lists; +import java.util.Collections; import java.util.Date; import java.util.List; import lombok.Getter; @@ -34,6 +35,7 @@ public class Admin implements ConfigLoadable, ConfigSavable, Validatable private Rank rank = Rank.SUPER_ADMIN; @Getter private final List ips = Lists.newArrayList(); + private final List backupCodes = Lists.newArrayList(); @Getter @Setter private Date lastLogin = new Date(); @@ -92,7 +94,8 @@ public class Admin implements ConfigLoadable, ConfigSavable, Validatable .append("- Potion Spy: ").append(potionSpy).append("\n") .append("- Admin Chat Format: ").append(acFormat).append("\n") .append("- Old Tags: ").append(oldTags).append("\n") - .append("- Log Stick: ").append(logStick); + .append("- Log Stick: ").append(logStick) + .append("- Backup Codes: ").append(backupCodes.size()).append("/10").append("\n"); return output.toString(); } @@ -113,6 +116,8 @@ public class Admin implements ConfigLoadable, ConfigSavable, Validatable rank = Rank.findRank(cs.getString("rank")); ips.clear(); ips.addAll(cs.getStringList("ips")); + backupCodes.clear(); + backupCodes.addAll(cs.getStringList("backupCodes")); lastLogin = FUtil.stringToDate(cs.getString("last_login")); loginMessage = cs.getString("login_message", null); discordID = cs.getString("discord_id", null); @@ -132,6 +137,7 @@ public class Admin implements ConfigLoadable, ConfigSavable, Validatable cs.set("active", active); cs.set("rank", rank.toString()); cs.set("ips", Lists.newArrayList(ips)); + cs.set("backupCodes", Lists.newArrayList(backupCodes)); cs.set("last_login", FUtil.dateToString(lastLogin)); cs.set("login_message", loginMessage); cs.set("discord_id", discordID); @@ -183,6 +189,22 @@ public class Admin implements ConfigLoadable, ConfigSavable, Validatable ips.clear(); } + public List getBackupCodes() + { + return Collections.unmodifiableList(backupCodes); + } + + public void setBackupCodes(List codes) + { + backupCodes.clear(); + backupCodes.addAll(codes); + } + + public void removeBackupCode(String code) + { + backupCodes.remove(code); + } + public void setActive(boolean active) { this.active = active; diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_chatcolor.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_chatcolor.java deleted file mode 100644 index 05e4ae4a..00000000 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_chatcolor.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.totalfreedom.totalfreedommod.command; - -import me.totalfreedom.totalfreedommod.playerverification.VPlayer; -import me.totalfreedom.totalfreedommod.rank.Rank; -import me.totalfreedom.totalfreedommod.util.FUtil; -import org.apache.commons.lang.StringUtils; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) -@CommandParameters(description = "Change your default chat color.", usage = "/ ") -public class Command_chatcolor extends FreedomCommand -{ - - @Override - public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) - { - if (args.length != 1) - { - return false; - } - - VPlayer vPlayer = plugin.pv.getVerificationPlayer(playerSender); - - if (args[0].equals("clear")) - { - vPlayer.setColor(null); - msg("Default chat color cleared."); - return true; - } - - if (args[0].equalsIgnoreCase("k") - || args[0].equalsIgnoreCase("0") - || args[0].equalsIgnoreCase("m")) - { - msg("You are not allowed to use that color as default."); - return true; - } - - ChatColor color = ChatColor.getByChar(args[0]); - if (color == null) - { - msg("Please enter a valid color. Example: a, 2, e"); - return true; - } - - vPlayer.setColor(color); - msg("Default chat color set to \"" + args[0] + ".\""); - return true; - } -} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java new file mode 100644 index 00000000..8e3260d9 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java @@ -0,0 +1,20 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_CONSOLE) +@CommandParameters(description = "Clear the discord message queue", usage = "/") +public class Command_cleardiscordqueue extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + plugin.dc.clearQueue(); + msg("Cleared the discord message queue."); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java index 617798d8..46bf9297 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java @@ -1,26 +1,106 @@ package me.totalfreedom.totalfreedommod.command; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FUtil; -import org.bukkit.Bukkit; -import org.bukkit.World; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @CommandPermissions(level = Rank.SUPER_ADMIN, source = SourceType.BOTH) -@CommandParameters(description = "Remove various server entities that may cause lag, such as dropped items, minecarts, and boats.", usage = "/", aliases = "ew,rd") +@CommandParameters(description = "Remove various server entities that may cause lag, such as dropped items, minecarts, and boats.", usage = "/ [name | -a]", aliases = "ew,rd") public class Command_entitywipe extends FreedomCommand { @Override public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - FUtil.adminAction(sender.getName(), "Removing all server entities", true); - int removed = plugin.ew.wipe(); - msg(removed + " entities removed."); + EntityType type = null; + String entityName = null; + boolean bypassBlacklist = false; + if (args.length > 0) + { + if (args[0].equals("-a")) + { + bypassBlacklist = true; + } + else + { + try + { + type = EntityType.valueOf(args[0].toUpperCase()); + } + catch (Exception e) + { + msg(args[0] + " is not a valid entity type.", ChatColor.RED); + return true; + } + if (!getAllEntities().contains(type)) + { + msg(FUtil.formatName(type.name()) + " is an entity, however: it is a mob.", ChatColor.RED); + return true; + } + } + } + + if (type != null) + { + entityName = FUtil.formatName(type.name()); + } + + FUtil.adminAction(sender.getName(), "Purging all " + (type != null ? entityName + "s" : "entities"), true); + int count; + if (type != null) + { + count = plugin.ew.wipeEntities(type); + } + else + { + count = plugin.ew.wipeEntities(bypassBlacklist); + } + msg(count + " " + (type != null ? entityName : "entities") + FUtil.showS(count) + " removed."); return true; } + + public static List getAllEntities() + { + List entityTypes = new ArrayList<>(); + for (EntityType entityType : EntityType.values()) + { + if (!Groups.MOB_TYPES.contains(entityType)) + { + entityTypes.add(entityType); + } + } + return entityTypes; + } + + public static List getAllEntityNames() + { + List names = new ArrayList<>(); + for (EntityType entityType : getAllEntities()) + { + names.add(entityType.name()); + } + return names; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + List names = getAllEntityNames(); + names.add("-a"); + if (args.length == 1) + { + return names; + } + + return Collections.emptyList(); + } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java index f4e3c2e1..3d17eb49 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java @@ -1,8 +1,8 @@ package me.totalfreedom.totalfreedommod.command; -import java.util.Random; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; import me.totalfreedom.totalfreedommod.playerverification.VPlayer; import me.totalfreedom.totalfreedommod.rank.Rank; import org.bukkit.ChatColor; @@ -24,6 +24,8 @@ public class Command_linkdiscord extends FreedomCommand return true; } + String code; + if (plugin.al.isAdmin(playerSender)) { Admin admin = plugin.al.getAdmin(playerSender); @@ -33,20 +35,33 @@ public class Command_linkdiscord extends FreedomCommand return true; } - if (Discord.LINK_CODES.containsValue(admin)) + if (Discord.ADMIN_LINK_CODES.containsValue(admin)) { - msg("Your linking code is " + ChatColor.GREEN + Discord.getCodeForAdmin(admin), ChatColor.AQUA); + code = Discord.getCodeForAdmin(admin); } else { - String code = ""; - Random random = new Random(); - for (int i = 0; i < 5; i++) - { - code += random.nextInt(10); - } - Discord.LINK_CODES.put(code, admin); - msg("Your linking code is " + ChatColor.GREEN + code, ChatColor.AQUA); + code = plugin.dc.generateCode(5); + Discord.ADMIN_LINK_CODES.put(code, admin); + } + } + else if (plugin.mbl.isMasterBuilder(playerSender)) + { + MasterBuilder masterBuilder = plugin.mbl.getMasterBuilder(playerSender); + if (masterBuilder.getDiscordID() != null) + { + msg("Your Minecraft account is already linked to a Discord account.", ChatColor.RED); + return true; + } + + if (Discord.MASTER_BUILDER_LINK_CODES.containsValue(masterBuilder)) + { + code = Discord.getCodeForMasterBuilder(masterBuilder); + } + else + { + code = plugin.dc.generateCode(5); + Discord.MASTER_BUILDER_LINK_CODES.put(code, masterBuilder); } } else @@ -60,21 +75,16 @@ public class Command_linkdiscord extends FreedomCommand if (Discord.PLAYER_LINK_CODES.containsValue(data)) { - msg("Your linking code is " + ChatColor.GREEN + Discord.getCodeForPlayer(data), ChatColor.AQUA); - msg("Take this code and DM the server bot (" + plugin.dc.formatBotTag() + ") the code (do not put anything else in the message, only the code)"); + code = Discord.getCodeForPlayer(data); } else { - String code = ""; - Random random = new Random(); - for (int i = 0; i < 5; i++) - { - code += random.nextInt(10); - } + code = plugin.dc.generateCode(5); Discord.PLAYER_LINK_CODES.put(code, data); - msg("Your linking code is " + ChatColor.GREEN + code, ChatColor.AQUA); } } + msg("Your linking code is " + ChatColor.AQUA + code, ChatColor.GREEN); + msg("Take this code and DM the server bot (" + plugin.dc.formatBotTag() + ") the code (do not put anything else in the message, only the code)"); return true; } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java index 159c9d55..e6797d8e 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java @@ -6,15 +6,10 @@ import java.util.List; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FUtil; import me.totalfreedom.totalfreedommod.util.Groups; -import org.apache.commons.lang.WordUtils; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @CommandPermissions(level = Rank.SUPER_ADMIN, source = SourceType.BOTH) @@ -41,43 +36,22 @@ public class Command_mobpurge extends FreedomCommand if (!Groups.MOB_TYPES.contains(type)) { - msg(WordUtils.capitalizeFully(type.name().replace("_", " ")) + " is an entity, however it is not a mob.", ChatColor.RED); + msg(FUtil.formatName(type.name()) + " is an entity, however it is not a mob.", ChatColor.RED); return true; } } if (type != null) { - mobName = WordUtils.capitalizeFully(type.name().replace("_", " ")); + mobName = FUtil.formatName(type.name()); } - FUtil.adminAction(sender.getName(), "Purging all " + (type != null ? mobName + "s" : "mobs"), true); - msg(purgeMobs(type) + " " + (type != null ? mobName : "mob") + "s removed."); + FUtil.adminAction(sender.getName(), "Purging all " + (type != null ? mobName + "s" : "mobs"), true); + int count = plugin.ew.purgeMobs(type); + msg(count + " " + (type != null ? mobName : "mob") + FUtil.showS(count) + " removed."); return true; } - public static int purgeMobs(EntityType type) - { - int removed = 0; - for (World world : Bukkit.getWorlds()) - { - for (Entity ent : world.getLivingEntities()) - { - if (ent instanceof LivingEntity && !(ent instanceof Player)) - { - if (type != null && !ent.getType().equals(type)) - { - continue; - } - ent.remove(); - removed++; - } - } - } - - return removed; - } - public static List getAllMobNames() { List names = new ArrayList<>(); diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java index 0b3da351..4afd44a7 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java @@ -1,6 +1,9 @@ package me.totalfreedom.totalfreedommod.command; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.rank.Rank; @@ -12,17 +15,14 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) -@CommandParameters(description = "Manage my admin entry", usage = "/ [-o ] | setlogin | clearlogin | setacformat | clearacformat> | oldtags | logstick | syncroles>") +@CommandPermissions(level = Rank.SUPER_ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Manage my admin entry", usage = "/ [-o ] | setlogin | clearlogin | setacformat | clearacformat> | oldtags | logstick | syncroles | genbackupcodes>") public class Command_myadmin extends FreedomCommand { @Override protected boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - checkPlayer(); - checkRank(Rank.SUPER_ADMIN); - if (args.length < 1) { return false; @@ -47,7 +47,7 @@ public class Command_myadmin extends FreedomCommand target = getAdmin(targetPlayer); if (target == null) { - msg("That player is not admin", ChatColor.RED); + msg("That player is not an admin", ChatColor.RED); return true; } @@ -84,6 +84,7 @@ public class Command_myadmin extends FreedomCommand target.addIp(targetIp); plugin.al.save(); + plugin.al.updateTables(); msg(counter + " IPs removed."); msg(targetPlayer, target.getIps().get(0) + " is now your only IP address"); @@ -240,10 +241,97 @@ public class Command_myadmin extends FreedomCommand return true; } + case "genbackupcodes": + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + else if (target.getDiscordID() == null || target.getDiscordID().isEmpty()) + { + msg("Discord verification is not enabled for you.", ChatColor.RED); + return true; + } + + msg("Generating backup codes...", ChatColor.GREEN); + + boolean generated = plugin.dc.sendBackupCodes(target); + + if (generated) + { + msg("Your backup codes have been sent to your discord account. They can be re-generated at anytime.", ChatColor.GREEN); + } + else + { + msg("Failed to generate backup codes, please contact a developer (preferably Seth)", ChatColor.RED); + } + return true; + default: { return false; } } } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + + List singleArguments = Arrays.asList("clearips", "setlogin", "setacformat"); + List doubleArguments = Arrays.asList("clearip", "clearlogin", "clearacformat", "oldtags", "logstick", "syncroles", "genbackupcodes"); + if (args.length == 1) + { + List options = new ArrayList<>(); + options.add("-o"); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + else if (args.length == 2) + { + if (args[0].equals("-o")) + { + return FUtil.getPlayerList(); + } + else + { + if (doubleArguments.contains(args[0])) + { + if (args[0].equals("clearip")) + { + List ips = plugin.al.getAdmin(sender).getIps(); + ips.remove(Ips.getIp(playerSender)); + return ips; + } + } + } + } + else if (args.length == 3) + { + if (args[0].equals("-o")) + { + List options = new ArrayList<>(); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + } + else if (args.length == 4) + { + if (args[0].equals("-o") && args[2].equals("clearip")) + { + Admin admin = plugin.al.getEntryByName(args[1]); + if (admin != null) + { + return admin.getIps(); + } + } + } + return FUtil.getPlayerList(); + } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mymasterbuilder.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mymasterbuilder.java new file mode 100644 index 00000000..8b36f837 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mymasterbuilder.java @@ -0,0 +1,237 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.pravian.aero.util.Ips; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Manage my Master Builder entry", usage = "/ [-o ] | genbackupcodes>", aliases = "mymb") +public class Command_mymasterbuilder extends FreedomCommand +{ + + @Override + protected boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + Player init = null; + MasterBuilder target = plugin.mbl.getMasterBuilder(playerSender); + Player targetPlayer = playerSender; + + // -o switch + if (args[0].equals("-o")) + { + if (!FUtil.canManageMasterBuilders(playerSender.getName())) + { + return noPerms(); + } + init = playerSender; + targetPlayer = getPlayer(args[1]); + if (targetPlayer == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + target = plugin.mbl.getMasterBuilder(playerSender); + if (target == null) + { + msg("That player is not a Master Builder", ChatColor.RED); + return true; + } + + // Shift 2 + args = Arrays.copyOfRange(args, 2, args.length); + if (args.length < 1) + { + return false; + } + } + + final String targetIp = Ips.getIp(targetPlayer); + + switch (args[0]) + { + case "clearips": + { + if (args.length != 1) + { + return false; // Double check: the player might mean "clearip" + } + + if (init == null) + { + FUtil.adminAction(sender.getName(), "Clearing my IPs", false); + } + else + { + FUtil.adminAction(sender.getName(), "Clearing " + target.getName() + "' IPs", true); + } + + int counter = target.getIps().size() - 1; + target.clearIPs(); + target.addIp(targetIp); + + plugin.mbl.save(); + plugin.mbl.updateTables(); + + msg(counter + " IPs removed."); + msg(targetPlayer, target.getIps().get(0) + " is now your only IP address"); + return true; + } + + case "clearip": + { + if (args.length != 2) + { + return false; // Double check: the player might mean "clearips" + } + + if (!target.getIps().contains(args[1])) + { + if (init == null) + { + msg("That IP is not registered to you."); + } + else + { + msg("That IP does not belong to that player."); + } + return true; + } + + if (targetIp.equals(args[1])) + { + if (init == null) + { + msg("You cannot remove your current IP."); + } + else + { + msg("You cannot remove that Master Builders's current IP."); + } + return true; + } + + FUtil.adminAction(sender.getName(), "Removing an IP" + (init == null ? "" : " from " + targetPlayer.getName() + "'s IPs"), true); + + target.removeIp(args[1]); + plugin.mbl.save(); + plugin.mbl.updateTables(); + + msg("Removed IP " + args[1]); + msg("Current IPs: " + StringUtils.join(target.getIps(), ", ")); + return true; + } + + case "genbackupcodes": + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + else if (target.getDiscordID() == null || target.getDiscordID().isEmpty()) + { + msg("Discord verification is not enabled for you.", ChatColor.RED); + return true; + } + + msg("Generating backup codes...", ChatColor.GREEN); + + boolean generated = plugin.dc.sendBackupCodes(target); + + if (generated) + { + msg("Your backup codes have been sent to your discord account. They can be re-generated at anytime.", ChatColor.GREEN); + } + else + { + msg("Failed to generate backup codes, please contact a developer (preferably Seth)", ChatColor.RED); + } + return true; + + default: + { + return false; + } + } + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + + if (!plugin.mbl.isMasterBuilder(playerSender) && !FUtil.canManageMasterBuilders(playerSender.getName())) + { + return Collections.emptyList(); + } + + List singleArguments = Arrays.asList("clearips"); + List doubleArguments = Arrays.asList("clearip", "genbackupcodes"); + if (args.length == 1) + { + List options = new ArrayList<>(); + options.add("-o"); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + else if (args.length == 2) + { + if (args[0].equals("-o")) + { + return FUtil.getPlayerList(); + } + else + { + if (doubleArguments.contains(args[0])) + { + if (args[0].equals("clearip")) + { + if (args[0].equals("clearip")) + { + List ips = plugin.mbl.getMasterBuilder(sender).getIps(); + ips.remove(Ips.getIp(playerSender)); + return ips; + } + } + } + } + } + else if (args.length == 3) + { + if (args[0].equals("-o")) + { + List options = new ArrayList<>(); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + } + else if (args.length == 4) + { + if (args[0].equals("-o") && args[2].equals("clearip")) + { + MasterBuilder masterBuilder = plugin.mbl.getEntryByName(args[1]); + if (masterBuilder != null) + { + return masterBuilder.getIps(); + } + } + } + return FUtil.getPlayerList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java new file mode 100644 index 00000000..1deb538b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java @@ -0,0 +1,135 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.playerverification.VPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SUPER_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Manage notes for a player", usage = "/ | remove | clear>") +public class Command_notes extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 2) + { + return false; + } + + VPlayer vPlayer; + + final Player player = getPlayer(args[0]); + if (player == null) + { + final PlayerData entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + vPlayer = plugin.pv.getVerificationPlayer(entry.getUsername()); + } + else + { + vPlayer = plugin.pv.getVerificationPlayer(player); + } + + if (args[1].equals("list")) + { + final StringBuilder noteList = new StringBuilder(); + noteList.append(ChatColor.GREEN + "Player notes for " + vPlayer.getName() + ":"); + int id = 1; + for (Map noteMap : vPlayer.getNotes()) + { + String admin = String.valueOf(noteMap.keySet().toArray()[0]); + String note = String.valueOf(noteMap.get(admin)); + String noteLine = id + ". " + admin + ": " + note; + noteList.append("\n" + ChatColor.GOLD + noteLine); + id++; + } + msg(noteList.toString()); + return true; + } + else if (args[1].equals("add")) + { + if (args.length < 3) + { + return false; + } + String note = StringUtils.join(ArrayUtils.subarray(args, 2, args.length), " "); + vPlayer.addNote(sender.getName(), note); + plugin.pv.saveVerificationData(vPlayer); + msg("Note added.", ChatColor.GREEN); + return true; + } + else if (args[1].equals("remove")) + { + if (args.length < 3) + { + return false; + } + int id; + try + { + id = Integer.valueOf(args[2]); + } + catch (NumberFormatException e) + { + msg("Invalid number: " + args[2], ChatColor.RED); + return true; + } + id--; + if (vPlayer.removeNote(id)) + { + plugin.pv.saveVerificationData(vPlayer); + msg("Note removed."); + } + else + { + msg("No note with the ID of " + args[2] + "exists.", ChatColor.RED); + } + return true; + } + else if (args[1].equals("clear")) + { + int count = vPlayer.getNotes().size(); + vPlayer.clearNotes(); + plugin.pv.saveVerificationData(vPlayer); + msg("Cleared " + count + " notes.", ChatColor.GREEN); + return true; + } + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return FUtil.getPlayerList(); + } + else if (args.length == 2) + { + return Arrays.asList("list", "add", "remove", "clear"); + } + else if (args.length > 2 && (args[1].equals("add"))) + { + return FUtil.getPlayerList(); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverify.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverify.java index c9b705a5..63d1f977 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverify.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverify.java @@ -13,7 +13,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) -@CommandParameters(description = "Manage your verification", usage = "/ <", aliases = "playerverification,pv") +@CommandParameters(description = "Manage your verification", usage = "/ ", aliases = "playerverification,pv") public class Command_playerverify extends FreedomCommand { @Override @@ -66,21 +66,19 @@ public class Command_playerverify extends FreedomCommand msg("The Discord verification system is currently disabled.", ChatColor.RED); return true; } - if (data.getEnabled()) + else if (data.getEnabled()) { msg("Discord verification is already enabled for you.", ChatColor.RED); return true; } + else if (data.getDiscordId() == null) + { + msg("Please link a discord account with /linkdiscord.", ChatColor.RED); + return true; + } data.setEnabled(true); plugin.pv.saveVerificationData(data); - if (data.getDiscordId() != null) - { - msg("Re-enabled Discord verification.", ChatColor.GREEN); - } - else - { - msg("Enabled Discord verification. Please type /linkdiscord to link a Discord account.", ChatColor.GREEN); - } + msg("Re-enabled Discord verification.", ChatColor.GREEN); return true; case "disable": @@ -99,7 +97,33 @@ public class Command_playerverify extends FreedomCommand boolean specified = target.getDiscordId() != null; msg(ChatColor.GRAY + "Discord Verification Enabled: " + (enabled ? ChatColor.GREEN + "true" : ChatColor.RED + "false")); msg(ChatColor.GRAY + "Discord ID: " + (specified ? ChatColor.GREEN + target.getDiscordId() : ChatColor.RED + "not set")); + msg(ChatColor.GRAY + "Backup Codes: " + data.getBackupCodes().size() + "/" + "10"); return true; + + case "genbackupcodes": + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + else if (!data.getEnabled()) + { + msg("Discord verification is not enabled for you.", ChatColor.RED); + return true; + } + + boolean generated = plugin.dc.sendBackupCodes(data); + + if (generated) + { + msg("Your backup codes have been sent to your discord account. They can be re-generated at anytime.", ChatColor.GREEN); + } + else + { + msg("Failed to generate backup codes, please contact a developer (preferably Seth)", ChatColor.RED); + } + return true; + default: return false; } @@ -109,7 +133,7 @@ public class Command_playerverify extends FreedomCommand { if (args.length == 1) { - return Arrays.asList("enable", "disable", "status", "clearips"); + return Arrays.asList("enable", "disable", "status", "clearips", "genbackupcodes"); } return Collections.emptyList(); diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java index 193814b8..9ad6c9d7 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java @@ -78,7 +78,7 @@ public class Command_purgeall extends FreedomCommand plugin.fm.setGlobalFreeze(false); // Remove all mobs - Command_mobpurge.purgeMobs(null); + plugin.ew.purgeMobs(null); return true; } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_realtime.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_realtime.java index 71150c4d..12f35600 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_realtime.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_realtime.java @@ -41,7 +41,7 @@ public class Command_realtime extends FreedomCommand player.setUtcOffset(tz); player.setRealTime(true); plugin.rt.enable(playerSender); - plugin.pv.save(); + plugin.pv.saveVerificationData(player); msg("Your in-game time is now synced with real time."); return true; } @@ -56,7 +56,7 @@ public class Command_realtime extends FreedomCommand player.setRealTime(false); msg("Your in-game time is no longer synced with real time."); plugin.rt.disable(playerSender); - plugin.pv.save(); + plugin.pv.saveVerificationData(player); return true; } return true; diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java index 24c5ff70..f6a382b1 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java @@ -1,12 +1,11 @@ package me.totalfreedom.totalfreedommod.command; import java.util.Date; -import java.util.Random; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.config.ConfigEntry; -import me.totalfreedom.totalfreedommod.discord.Discord; import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.playerverification.VPlayer; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FUtil; import net.pravian.aero.util.Ips; @@ -44,7 +43,7 @@ public class Command_verify extends FreedomCommand plugin.pl.getPlayer(player).getFreezeData().setFrozen(false); player.sendMessage(ChatColor.GRAY + "You have been unfrozen."); } - plugin.pv.verifyPlayer(player); + plugin.pv.verifyPlayer(player, null); plugin.rm.updateDisplay(player); return true; } @@ -62,7 +61,7 @@ public class Command_verify extends FreedomCommand return true; } - if (!plugin.pv.isPlayerImpostor(playerSender) && !plugin.al.isAdminImpostor(playerSender)) + if (!plugin.pv.isPlayerImpostor(playerSender) && !plugin.al.isAdminImpostor(playerSender) && !plugin.mbl.isMasterBuilderImpostor(playerSender)) { msg("You are not an impostor, therefore you do not need to verify.", ChatColor.RED); return true; @@ -70,7 +69,17 @@ public class Command_verify extends FreedomCommand String discordId = ""; - if (plugin.al.isAdminImpostor(playerSender)) + if (plugin.pv.isPlayerImpostor(playerSender)) + { + VPlayer vPlayer = plugin.pv.getVerificationPlayer(playerSender); + if (vPlayer.getDiscordId() == null) + { + msg("You do not have a Discord account linked to your Minecraft account, please verify the manual way.", ChatColor.RED); + return true; + } + discordId = vPlayer.getDiscordId(); + } + else if (plugin.al.isAdminImpostor(playerSender)) { Admin admin = plugin.al.getEntryByName(playerSender.getName()); if (admin.getDiscordID() == null) @@ -80,48 +89,109 @@ public class Command_verify extends FreedomCommand } discordId = admin.getDiscordID(); } - - if (plugin.pv.isPlayerImpostor(playerSender)) + else if (plugin.mbl.isMasterBuilderImpostor(playerSender)) { - if (plugin.pv.getVerificationPlayer(playerSender).getDiscordId() == null) + MasterBuilder masterBuilder = plugin.mbl.getEntryByName(playerSender.getName()); + if (masterBuilder.getDiscordID() == null) { msg("You do not have a Discord account linked to your Minecraft account, please verify the manual way.", ChatColor.RED); return true; } - discordId = plugin.pv.getVerificationPlayer(playerSender).getDiscordId(); + discordId = masterBuilder.getDiscordID(); } if (args.length < 1) { - String code = ""; - Random random = new Random(); - for (int i = 0; i < 10; i++) + String code = plugin.dc.generateCode(10); + if (plugin.pv.isPlayerImpostor(playerSender)) { - code += random.nextInt(10); + VPlayer vPlayer = plugin.pv.getVerificationPlayer(playerSender); + plugin.dc.addPlayerVerificationCode(code, vPlayer); } - Discord.VERIFY_CODES.add(code); - Discord.bot.getUserById(discordId).openPrivateChannel().complete().sendMessage("A user with the IP `" + Ips.getIp(playerSender) + "` has sent a verification request. Please run the following in-game command: `/verify " + code + "`").complete(); + else if (plugin.al.isAdminImpostor(playerSender)) + { + Admin admin = plugin.al.getEntryByName(playerSender.getName()); + plugin.dc.addAdminVerificationCode(code, admin); + } + else if (plugin.mbl.isMasterBuilderImpostor(playerSender)) + { + MasterBuilder masterBuilder = plugin.mbl.getEntryByName(playerSender.getName()); + plugin.dc.addMasterBuilderVerificationCode(code, masterBuilder); + } + plugin.dc.bot.getUserById(discordId).openPrivateChannel().complete().sendMessage("A user with the IP `" + Ips.getIp(playerSender) + "` has sent a verification request. Please run the following in-game command: `/verify " + code + "`").complete(); msg("A verification code has been sent to your account, please copy the code and run /verify ", ChatColor.GREEN); } else { String code = args[0]; - if (!Discord.VERIFY_CODES.contains(code)) + String backupCode = null; + + if (plugin.pv.isPlayerImpostor(playerSender)) { - msg("You have entered an invalid verification code", ChatColor.RED); + VPlayer vPlayer = plugin.pv.getVerificationPlayer(playerSender); + VPlayer mapPlayer = plugin.dc.getPlayerVerificationCodes().get(code); + if (mapPlayer == null) + { + if (!vPlayer.getBackupCodes().contains(plugin.dc.getMD5(code))) + { + msg("You have entered an invalid verification code", ChatColor.RED); + return true; + } + else + { + backupCode = plugin.dc.getMD5(code); + } + } + else + { + plugin.dc.removePlayerVerificationCode(code); + } + + final FPlayer fPlayer = plugin.pl.getPlayer(playerSender); + FUtil.bcastMsg(playerSender.getName() + " has verified!", ChatColor.GOLD); + plugin.rm.updateDisplay(playerSender); + playerSender.setOp(true); + msg(YOU_ARE_OP); + if (fPlayer.getFreezeData().isFrozen()) + { + fPlayer.getFreezeData().setFrozen(false); + msg("You have been unfrozen."); + } + plugin.pv.verifyPlayer(playerSender, backupCode); return true; } - - if (plugin.al.isAdminImpostor(playerSender)) + else if (plugin.al.isAdminImpostor(playerSender)) { Admin admin = plugin.al.getEntryByName(playerSender.getName()); - Discord.VERIFY_CODES.remove(code); + Admin mapAdmin = plugin.dc.getAdminVerificationCodes().get(code); + if (mapAdmin == null) + { + if (!admin.getBackupCodes().contains(plugin.dc.getMD5(code))) + { + msg("You have entered an invalid verification code", ChatColor.RED); + return true; + } + else + { + backupCode = plugin.dc.getMD5(code); + } + } + else + { + plugin.dc.removeAdminVerificationCode(code); + } + FUtil.bcastMsg(playerSender.getName() + " has verified!", ChatColor.GOLD); FUtil.adminAction(ConfigEntry.SERVER_NAME.getString(), "Re-adding " + admin.getName() + " to the admin list", true); admin.setName(playerSender.getName()); admin.addIp(Ips.getIp(playerSender)); + if (backupCode != null) + { + admin.removeBackupCode(backupCode); + } + if (!plugin.mbl.isMasterBuilder(playerSender)) { MasterBuilder masterBuilder = null; @@ -161,11 +231,38 @@ public class Command_verify extends FreedomCommand } return true; } - - if (plugin.pv.isPlayerImpostor(playerSender)) + else if (plugin.mbl.isMasterBuilderImpostor(playerSender)) { + MasterBuilder masterBuilder = plugin.mbl.getEntryByName(playerSender.getName()); + MasterBuilder mapMasterBuilder = plugin.dc.getMasterBuilderVerificationCodes().get(code); + if (mapMasterBuilder == null) + { + if (!masterBuilder.getBackupCodes().contains(plugin.dc.getMD5(code))) + { + msg("You have entered an invalid verification code", ChatColor.RED); + return true; + } + else + { + backupCode = plugin.dc.getMD5(code); + } + } + else + { + plugin.dc.removeMasterBuilderVerificationCode(code); + } + + if (backupCode != null) + { + masterBuilder.removeBackupCode(backupCode); + } + final FPlayer fPlayer = plugin.pl.getPlayer(playerSender); FUtil.bcastMsg(playerSender.getName() + " has verified!", ChatColor.GOLD); + masterBuilder.setLastLogin(new Date()); + masterBuilder.addIp(Ips.getIp(playerSender)); + plugin.mbl.save(); + plugin.mbl.updateTables(); plugin.rm.updateDisplay(playerSender); playerSender.setOp(true); msg(YOU_ARE_OP); @@ -174,7 +271,6 @@ public class Command_verify extends FreedomCommand fPlayer.getFreezeData().setFrozen(false); msg("You have been unfrozen."); } - plugin.pv.verifyPlayer(playerSender); return true; } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java index 36751d67..379ad368 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java @@ -2,17 +2,24 @@ package me.totalfreedom.totalfreedommod.discord; import com.earth2me.essentials.User; import com.google.common.base.Strings; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.time.Instant; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; import javax.security.auth.login.LoginException; import me.totalfreedom.totalfreedommod.FreedomService; -import me.totalfreedom.totalfreedommod.GameRuleHandler; import me.totalfreedom.totalfreedommod.TotalFreedomMod; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; import me.totalfreedom.totalfreedommod.playerverification.VPlayer; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FLog; @@ -22,13 +29,17 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.PrivateChannel; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.SelfUser; import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.internal.utils.concurrent.CountingThreadFactory; import net.pravian.aero.util.StringUtils; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.WordUtils; import org.bukkit.GameRule; import org.bukkit.entity.Player; @@ -40,12 +51,20 @@ import org.bukkit.event.player.PlayerQuitEvent; public class Discord extends FreedomService { - public static HashMap LINK_CODES = new HashMap<>(); - public static HashMap PLAYER_LINK_CODES = new HashMap(); - public static List VERIFY_CODES = new ArrayList(); + public static HashMap PLAYER_LINK_CODES = new HashMap<>(); + public static HashMap PLAYER_VERIFICATION_CODES = new HashMap<>(); + public static HashMap ADMIN_LINK_CODES = new HashMap<>(); + public static HashMap ADMIN_VERIFICATION_CODES = new HashMap<>(); + public static HashMap MASTER_BUILDER_LINK_CODES = new HashMap<>(); + public static HashMap MASTER_BUILDER_VERIFICATION_CODES = new HashMap<>(); + public ScheduledThreadPoolExecutor RATELIMIT_EXECUTOR = new ScheduledThreadPoolExecutor(5, new CountingThreadFactory(this::poolIdentifier, "RateLimit")); + public List> sentMessages = new ArrayList<>(); + public static JDA bot = null; public Boolean enabled = false; + Random random = new Random(); + public Discord(TotalFreedomMod plugin) { super(plugin); @@ -67,12 +86,13 @@ public class Discord extends FreedomService } try { - + RATELIMIT_EXECUTOR.setRemoveOnCancelPolicy(true); bot = new JDABuilder(AccountType.BOT) .setToken(ConfigEntry.DISCORD_TOKEN.getString()) .addEventListeners(new PrivateMessageListener()) .addEventListeners(new DiscordToMinecraftListener()) .setAutoReconnect(true) + .setRateLimitPool(RATELIMIT_EXECUTOR) .addEventListeners(new ListenerAdapter() { @Override @@ -99,11 +119,31 @@ public class Discord extends FreedomService } + public String poolIdentifier() + { + return "JDA"; + } + + public void clearQueue() + { + for (CompletableFuture messages : sentMessages) + { + if (!messages.isDone()) + { + messages.cancel(true); + } + } + sentMessages.clear(); + messageChatChannel("**Message queue cleared**"); + } + // Do no ask why this is here. I spent two hours trying to make a simple thing work - public class StartEvent { + public class StartEvent + { private final JDA api; - public StartEvent(JDA api) { + public StartEvent(JDA api) + { this.api = api; } @@ -113,6 +153,181 @@ public class Discord extends FreedomService } } + public boolean sendBackupCodes(VPlayer vPlayer) + { + List codes = generateBackupCodes(); + List encryptedCodes = generateEncryptedBackupCodes(codes); + net.dv8tion.jda.api.entities.User user = bot.getUserById(vPlayer.getDiscordId()); + File file = generateBackupCodesFile(vPlayer.getName(), codes); + if (file == null) + { + return false; + } + PrivateChannel privateChannel = user.openPrivateChannel().complete(); + privateChannel.sendMessage("Do not share these codes with anyone as they can be used to impose as you.").addFile(file).complete(); + vPlayer.setBackupCodes(encryptedCodes); + plugin.pv.saveVerificationData(vPlayer); + file.delete(); + return true; + } + + public boolean sendBackupCodes(Admin admin) + { + List codes = generateBackupCodes(); + List encryptedCodes = generateEncryptedBackupCodes(codes); + net.dv8tion.jda.api.entities.User user = bot.getUserById(admin.getDiscordID()); + File file = generateBackupCodesFile(admin.getName(), codes); + if (file == null) + { + return false; + } + PrivateChannel privateChannel = user.openPrivateChannel().complete(); + privateChannel.sendMessage("Do not share these codes with anyone as they can be used to impose as you.").addFile(file).complete(); + admin.setBackupCodes(encryptedCodes); + plugin.al.save(); + plugin.al.updateTables(); + file.delete(); + return true; + } + + public boolean sendBackupCodes(MasterBuilder masterBuilder) + { + List codes = generateBackupCodes(); + List encryptedCodes = generateEncryptedBackupCodes(codes); + net.dv8tion.jda.api.entities.User user = bot.getUserById(masterBuilder.getDiscordID()); + File file = generateBackupCodesFile(masterBuilder.getName(), codes); + if (file == null) + { + return false; + } + PrivateChannel privateChannel = user.openPrivateChannel().complete(); + privateChannel.sendMessage("Do not share these codes with anyone as they can be used to impose as you.").addFile(file).complete(); + masterBuilder.setBackupCodes(encryptedCodes); + plugin.mbl.save(); + plugin.mbl.updateTables(); + file.delete(); + return true; + } + + public List generateBackupCodes() + { + List codes = new ArrayList<>(); + for (int i = 0; i < 10; i++) + { + codes.add(randomString(10)); + } + return codes; + } + + public String randomString(int size) + { + List chars = Arrays.asList("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz".split("(?!^)")); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < size; i++) + { + stringBuilder.append(chars.get(random.nextInt(chars.size()))); + } + return stringBuilder.toString(); + } + + public String generateCode(int size) + { + String code = ""; + Random random = new Random(); + for (int i = 0; i < size; i++) + { + code += random.nextInt(10); + } + return code; + } + + public List generateEncryptedBackupCodes(List codes) + { + List encryptedCodes = new ArrayList<>(); + for (String code : codes) + { + encryptedCodes.add(getMD5(code)); + } + return encryptedCodes; + } + + public File generateBackupCodesFile(String name, List codes) + { + StringBuilder text = new StringBuilder(); + text.append("Below are your backup codes for use on TotalFreedom in the event you lose access to your discord account.\n") + .append("Simply pick a code, and run /verify on the server. Each code is one use, so be sure to cross it off once you use it.\n") + .append("To generate new codes, simply run /generatebackupcodes\n\n"); + + for (String code : codes) + { + text.append(code + "\n"); + } + + String fileUrl = plugin.getDataFolder().getAbsolutePath() + "/TF-Backup-Codes-" + name + ".txt"; + try + { + FileWriter fileWriter = new FileWriter(fileUrl); + fileWriter.write(text.toString()); + fileWriter.close(); + } + catch (IOException e) + { + FLog.severe("Failed to generate backup codes file: " + e.toString()); + return null; + } + return new File(fileUrl); + } + + public static String getMD5(String string) + { + return DigestUtils.md5Hex(string); + } + + public void addPlayerVerificationCode(String code, VPlayer vPlayer) + { + PLAYER_VERIFICATION_CODES.put(code, vPlayer); + } + + public void removePlayerVerificationCode(String code) + { + PLAYER_VERIFICATION_CODES.remove(code); + } + + public HashMap getPlayerVerificationCodes() + { + return PLAYER_VERIFICATION_CODES; + } + + public void addAdminVerificationCode(String code, Admin admin) + { + ADMIN_VERIFICATION_CODES.put(code, admin); + } + + public void removeAdminVerificationCode(String code) + { + ADMIN_VERIFICATION_CODES.remove(code); + } + + public HashMap getAdminVerificationCodes() + { + return ADMIN_VERIFICATION_CODES; + } + + public void addMasterBuilderVerificationCode(String code, MasterBuilder masterBuilder) + { + MASTER_BUILDER_VERIFICATION_CODES.put(code, masterBuilder); + } + + public void removeMasterBuilderVerificationCode(String code) + { + MASTER_BUILDER_VERIFICATION_CODES.remove(code); + } + + public HashMap getMasterBuilderVerificationCodes() + { + return MASTER_BUILDER_VERIFICATION_CODES; + } + @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { @@ -157,7 +372,8 @@ public class Discord extends FreedomService } if (enabled && !chat_channel_id.isEmpty()) { - bot.getTextChannelById(chat_channel_id).sendMessage(message).queue(); + CompletableFuture sentMessage = bot.getTextChannelById(chat_channel_id).sendMessage(message).submit(true); + sentMessages.add(sentMessage); } } @@ -169,9 +385,9 @@ public class Discord extends FreedomService public static String getCodeForAdmin(Admin admin) { - for (String code : LINK_CODES.keySet()) + for (String code : ADMIN_LINK_CODES.keySet()) { - if (LINK_CODES.get(code).equals(admin)) + if (ADMIN_LINK_CODES.get(code).equals(admin)) { return code; } @@ -191,6 +407,18 @@ public class Discord extends FreedomService return null; } + public static String getCodeForMasterBuilder(MasterBuilder masterBuilder) + { + for (String code : MASTER_BUILDER_LINK_CODES.keySet()) + { + if (MASTER_BUILDER_LINK_CODES.get(code).equals(masterBuilder)) + { + return code; + } + } + return null; + } + @Override protected void onStop() { diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java index dc1a6836..efca196a 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java @@ -2,6 +2,7 @@ package me.totalfreedom.totalfreedommod.discord; import me.totalfreedom.totalfreedommod.TotalFreedomMod; import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.masterbuilder.MasterBuilder; import me.totalfreedom.totalfreedommod.playerverification.VPlayer; import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; @@ -16,26 +17,42 @@ public class PrivateMessageListener extends ListenerAdapter if (event.getMessage().getContentRaw().matches("[0-9][0-9][0-9][0-9][0-9]")) { String code = event.getMessage().getContentRaw(); - if (Discord.LINK_CODES.get(code) != null) + String name; + if (Discord.ADMIN_LINK_CODES.get(code) != null) { - Admin admin = Discord.LINK_CODES.get(code); + Admin admin = Discord.ADMIN_LINK_CODES.get(code); + name = admin.getName(); admin.setDiscordID(event.getMessage().getAuthor().getId()); - Discord.LINK_CODES.remove(code); - event.getChannel().sendMessage("Link successful. Now this Discord account is linked with your Minecraft account `" + admin.getName() + "`.\n" - + "Now when you are an impostor on the server, you may use `/verify` to verify.").complete(); + TotalFreedomMod.plugin().al.save(); + TotalFreedomMod.plugin().al.updateTables(); + Discord.ADMIN_LINK_CODES.remove(code); Discord.syncRoles(admin); } - if (Discord.PLAYER_LINK_CODES.get(code) != null) + else if (Discord.PLAYER_LINK_CODES.get(code) != null) { VPlayer player = Discord.PLAYER_LINK_CODES.get(code); + name = player.getName(); player.setDiscordId(event.getMessage().getAuthor().getId()); player.setEnabled(true); TotalFreedomMod.plugin().pv.saveVerificationData(player); Discord.PLAYER_LINK_CODES.remove(code); - event.getChannel().sendMessage("Link successful. Now this Discord account is linked with your Minecraft account `" + player.getName() + "`.\n" - + "Now when you are an impostor on the server, you may use `/verify` to verify.").complete(); } + else if (Discord.MASTER_BUILDER_LINK_CODES.get(code) != null) + { + MasterBuilder masterBuilder = Discord.MASTER_BUILDER_LINK_CODES.get(code); + name = masterBuilder.getName(); + masterBuilder.setDiscordID(event.getMessage().getAuthor().getId()); + TotalFreedomMod.plugin().mbl.save(); + TotalFreedomMod.plugin().mbl.updateTables(); + Discord.MASTER_BUILDER_LINK_CODES.remove(code); + } + else + { + return; + } + event.getChannel().sendMessage("Link successful. Now this Discord account is linked with your Minecraft account **" + name + "**.\n" + + "Now when you are an impostor on the server, you may use `/verify` to verify.").complete(); } } } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/masterbuilder/MasterBuilder.java b/src/main/java/me/totalfreedom/totalfreedommod/masterbuilder/MasterBuilder.java index c21939cd..f4a8f4af 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/masterbuilder/MasterBuilder.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/masterbuilder/MasterBuilder.java @@ -1,6 +1,7 @@ package me.totalfreedom.totalfreedommod.masterbuilder; import com.google.common.collect.Lists; +import java.util.Collections; import java.util.Date; import java.util.List; import lombok.Getter; @@ -25,6 +26,7 @@ public class MasterBuilder implements ConfigLoadable, ConfigSavable, Validatable private String name; @Getter private final List ips = Lists.newArrayList(); + private final List backupCodes = Lists.newArrayList(); @Getter @Setter private Date lastLogin = new Date(); @@ -62,7 +64,8 @@ public class MasterBuilder implements ConfigLoadable, ConfigSavable, Validatable .append("- Last Login: ").append(FUtil.dateToString(lastLogin)).append("\n") .append("- Discord ID: ").append(discordID).append("\n") .append("- Tag: ").append(tag).append("\n") - .append("- Clear Chat Opt Out: ").append(clearChatOptOut); + .append("- Clear Chat Opt Out: ").append(clearChatOptOut) + .append("- Backup Codes: ").append(backupCodes.size()).append("/10").append("\n"); return output.toString(); } @@ -81,6 +84,8 @@ public class MasterBuilder implements ConfigLoadable, ConfigSavable, Validatable name = cs.getString("username", configKey); ips.clear(); ips.addAll(cs.getStringList("ips")); + backupCodes.clear(); + backupCodes.addAll(cs.getStringList("backupCodes")); lastLogin = FUtil.stringToDate(cs.getString("last_login")); discordID = cs.getString("discord_id", null); tag = cs.getString("tag", null); @@ -93,6 +98,7 @@ public class MasterBuilder implements ConfigLoadable, ConfigSavable, Validatable Validate.isTrue(isValid(), "Could not save master builder entry: " + name + ". Entry not valid!"); cs.set("username", name); cs.set("ips", Lists.newArrayList(ips)); + cs.set("backupCodes", Lists.newArrayList(backupCodes)); cs.set("last_login", FUtil.dateToString(lastLogin)); cs.set("discord_id", discordID); cs.set("tag", tag); @@ -125,6 +131,22 @@ public class MasterBuilder implements ConfigLoadable, ConfigSavable, Validatable ips.clear(); } + public List getBackupCodes() + { + return Collections.unmodifiableList(backupCodes); + } + + public void setBackupCodes(List codes) + { + backupCodes.clear(); + backupCodes.addAll(codes); + } + + public void removeBackupCode(String code) + { + backupCodes.remove(code); + } + @Override public boolean isValid() { diff --git a/src/main/java/me/totalfreedom/totalfreedommod/playerverification/PlayerVerification.java b/src/main/java/me/totalfreedom/totalfreedommod/playerverification/PlayerVerification.java index 40c0a492..17e290fa 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/playerverification/PlayerVerification.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/playerverification/PlayerVerification.java @@ -51,7 +51,7 @@ public class PlayerVerification extends FreedomService && !vPlayer.getIps().contains(Ips.getIp(player)); } - public void verifyPlayer(Player player) + public void verifyPlayer(Player player, String backupCode) { if (!isPlayerImpostor(player)) { @@ -59,6 +59,10 @@ public class PlayerVerification extends FreedomService } VPlayer vPlayer = getVerificationPlayer(player); + if (backupCode != null) + { + vPlayer.removeBackupCode(backupCode); + } vPlayer.addIp(Ips.getIp(player)); dataMap.put(player.getName(), vPlayer); YamlConfig config = getConfig(vPlayer); diff --git a/src/main/java/me/totalfreedom/totalfreedommod/playerverification/VPlayer.java b/src/main/java/me/totalfreedom/totalfreedommod/playerverification/VPlayer.java index f28b0b1d..4f1d5535 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/playerverification/VPlayer.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/playerverification/VPlayer.java @@ -2,14 +2,16 @@ package me.totalfreedom.totalfreedommod.playerverification; import com.google.common.collect.Lists; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import lombok.Getter; import lombok.Setter; +import me.totalfreedom.totalfreedommod.util.FLog; import net.pravian.aero.base.ConfigLoadable; import net.pravian.aero.base.ConfigSavable; import net.pravian.aero.base.Validatable; import org.apache.commons.lang.Validate; -import org.bukkit.ChatColor; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; @@ -17,6 +19,8 @@ public class VPlayer implements ConfigLoadable, ConfigSavable, Validatable { private final List ips = Lists.newArrayList(); + private final List> notes = Lists.newArrayList(); + private final List backupCodes = Lists.newArrayList(); @Getter @Setter private String name; @@ -40,9 +44,6 @@ public class VPlayer implements ConfigLoadable, ConfigSavable, Validatable private String rideMode = "ask"; @Getter @Setter - private ChatColor color = null; - @Getter - @Setter private int utcOffset = 0; @Getter @Setter @@ -61,15 +62,18 @@ public class VPlayer implements ConfigLoadable, ConfigSavable, Validatable @Override public void loadFrom(ConfigurationSection cs) { - name = cs.getString("username", name); + name = cs.getString("name", name); ips.clear(); ips.addAll(cs.getStringList("ips")); + notes.clear(); + notes.addAll(cs.getMapList("notes")); + backupCodes.clear(); + backupCodes.addAll(cs.getStringList("backupCodes")); discordId = cs.getString("discordId", null); enabled = cs.getBoolean("enabled", false); tag = cs.getString("tag", null); clearChatOptOut = cs.getBoolean("clearChatOptOut", false); rideMode = cs.getString("rideMode", rideMode); - color = ChatColor.getByChar(String.valueOf(cs.get("color"))); utcOffset = cs.getInt("utcOffset", 0); realTime = cs.getBoolean("realTime", false); } @@ -83,9 +87,10 @@ public class VPlayer implements ConfigLoadable, ConfigSavable, Validatable cs.set("enabled", enabled); cs.set("tag", tag); cs.set("ips", Lists.newArrayList(ips)); + cs.set("notes", Lists.newArrayList(notes)); + cs.set("backupCodes", Lists.newArrayList(backupCodes)); cs.set("clearChatOptOut", clearChatOptOut); cs.set("rideMode", rideMode); - cs.set("color", color == null ? null : color.getChar()); cs.set("utcOffset", utcOffset); cs.set("realTime", realTime); } @@ -105,6 +110,54 @@ public class VPlayer implements ConfigLoadable, ConfigSavable, Validatable ips.remove(ip); } + public List> getNotes() + { + return Collections.unmodifiableList(notes); + } + + public void clearNotes() + { + notes.clear(); + } + + public List getBackupCodes() + { + return Collections.unmodifiableList(backupCodes); + } + + public void setBackupCodes(List codes) + { + backupCodes.clear(); + backupCodes.addAll(codes); + } + + public void removeBackupCode(String code) + { + FLog.info("fuck"); + backupCodes.remove(code); + } + + public void addNote(String adder, String note) + { + + Map noteMap = new HashMap<>(); + noteMap.put(adder, note); + notes.add(noteMap); + } + + public boolean removeNote(int id) throws IndexOutOfBoundsException + { + try + { + notes.remove(id); + } + catch (IndexOutOfBoundsException e) + { + return false; + } + return true; + } + @Override public boolean isValid() { diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java index cf1b77d6..f1507456 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java @@ -26,6 +26,7 @@ import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.shop.ShopItem; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -124,6 +125,20 @@ public class FUtil return ConfigEntry.SERVER_OWNERS.getStringList().contains(name) || ConfigEntry.SERVER_EXECUTIVES.getStringList().contains(name) || ConfigEntry.SERVER_MASTER_BUILDER_MANAGEMENT.getStringList().contains(name); } + public static String formatName(String name) + { + return WordUtils.capitalizeFully(name.replace("_", " ")); + } + + public static String showS(int count) + { + if (count == 1) + { + return ""; + } + return "s"; + } + public static List getPlayerList() { List names = new ArrayList<>();