package com.lifeknight.relaymcbungeemain.player; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.lifeknight.relaymcbungeemain.Main; import com.lifeknight.relaymcbungeemain.commands.CommandUtilities; import com.lifeknight.relaymcbungeemain.commands.game.DuelAcceptCommand; import com.lifeknight.relaymcbungeemain.queue.GameServer; import com.lifeknight.relaymcbungeemain.queue.Queue; import com.lifeknight.relaymcbungeemain.utilities.*; import com.lifeknight.relayutils.RelayUtils; import com.lifeknight.relayutils.basic.Miscellaneous; import com.lifeknight.relayutils.basic.SwearUtils; import com.lifeknight.relayutils.basic.Text; import com.lifeknight.relayutils.data.QueueData; import com.lifeknight.relayutils.filter.*; import com.lifeknight.relayutils.game.GameDetails; import com.lifeknight.relayutils.game.GameType; import com.lifeknight.relayutils.game.League; import com.lifeknight.relayutils.game.MainGame; import com.lifeknight.relayutils.network.Command; import com.lifeknight.relayutils.network.Message; import com.lifeknight.relayutils.player.ExtraData; import com.lifeknight.relayutils.player.Group; import com.lifeknight.relayutils.player.Nick; import com.lifeknight.relayutils.player.cosmetics.Cosmetic; import com.lifeknight.relayutils.player.cosmetics.GameCosmetic; import com.lifeknight.relayutils.player.data.ProfileColumn; import com.lifeknight.relayutils.player.data.SmartDatabase; import com.lifeknight.relayutils.player.punishments.Punishment; import com.lifeknight.relayutils.player.punishments.PunishmentType; import com.lifeknight.relayutils.utilities.ComponentBuilder; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.Connection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.ScoreboardDisplay; import net.md_5.bungee.protocol.packet.ScoreboardObjective; import net.md_5.bungee.protocol.packet.ScoreboardScore; import org.bukkit.Sound; import java.awt.*; import java.lang.instrument.Instrumentation; import java.sql.ResultSet; import java.util.List; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.lifeknight.relayutils.basic.Text.*; import static net.md_5.bungee.api.ChatColor.*; public class SmartPlayer { public static final Map SMART_PLAYERS = new HashMap<>(); public static final List ONLINE_PLAYERS = new ArrayList<>(); public static final List BANNED_IPS = new ArrayList<>(); private static final List LIMBO_QUEUE = new ArrayList<>(); private final long inception; private final UUID uuid; private ProxiedPlayer proxiedPlayer; private long lastDisconnectTime; private final long createTime = System.currentTimeMillis(); private final List queuedMessages = new ArrayList<>(); private boolean hasJoined = false; private String lastName = null; private long firstJoinTime = System.currentTimeMillis(); private long previousLastJoinTime = System.currentTimeMillis(); private long joinTime = System.currentTimeMillis(); private boolean isNewLogin; private List friends = new ArrayList<>(); private List blockedPlayers = new ArrayList<>(); private List previousUsernames = new ArrayList<>(); private List groups = Miscellaneous.getList(Group.DEFAULT); private long credits = 0; private long xp = 0; private long timePlayed = 0; private List cosmetics = new ArrayList<>(); private List activeCosmetics = new ArrayList<>(); private Map> gameCosmetics = new HashMap<>(); private Map> activeGameCosmetics = new HashMap<>(); private List punishments = new ArrayList<>(); private boolean isNicked = false; private Nick nick = null; private ChatFilter chatFilter = ChatFilter.MEDIUM; private MessageFilter messageFilter = MessageFilter.FRIENDS_OF_FRIENDS; private FriendFilter friendFilter = FriendFilter.ANYBODY; private PartyFilter partyFilter = PartyFilter.ANYBODY; private DuelFilter duelFilter = DuelFilter.ANYBODY; private SmartPlayer lastMessageSender = null; private final Map lastMessages = new HashMap<>(); private List latestMessagesWithin3Seconds = new ArrayList<>(); private int commandsInPast5Seconds = 0; private final List messages = new ArrayList<>(); private ExtraData extraData = new ExtraData(new JsonObject()); private GameType currentQueue; private boolean hasUpdatedQueueScoreboard; private ChatType chatType = ChatType.ALL; private Party currentParty = null; private List invitedParties = new ArrayList<>(); private Map invitedDuels = new HashMap<>(); private List receivedFriendRequests = new ArrayList<>(); private Server lastGameServer; public GameType lastGameType; private Server lastServer; private ServerInfo connectingServer = null; private Server lastHubServer; private final Map gameExperience = new HashMap<>(); private final Map gameElo = new HashMap<>(); private int manhuntQuits = 0; private boolean isBannedFromManhunt = false; private long lastReportTime; private boolean hasReadData = false; private boolean receiveReportMessages = true; private long lastLevelUp; private SmartPlayer rematchOpponent = null; private GameType rematchGameType = null; private QueueData rematchQueueData = null; private boolean acceptRematch = false; private QueueData queueData = null; private long discordId; private int discordEarned = 0; private long lastDiscordMessage = System.currentTimeMillis(); private long lastConnectTime; public SmartPlayer(ProxiedPlayer proxiedPlayer) { this.inception = System.currentTimeMillis(); this.proxiedPlayer = proxiedPlayer; this.uuid = this.proxiedPlayer.getUniqueId(); SMART_PLAYERS.put(proxiedPlayer.getUniqueId(), this); removeUnneededSmartPlayers(); } public static void removeUnneededSmartPlayers() { for (SmartPlayer value : Miscellaneous.getList(SMART_PLAYERS.values())) { if (!value.isOnline() && !value.hasOnlineFriend() && Miscellaneous.enoughTimeHasPassed(value.inception, 10, TimeUnit.MINUTES) && Miscellaneous.enoughTimeHasPassed(value.createTime, 3, TimeUnit.HOURS)) { SMART_PLAYERS.remove(value.getUUID()); if (RelayUtils.log) { Main.info("Removed SmartPlayer %s for inactivity.", value.getName()); } } } } public SmartPlayer(UUID uuid) { this.inception = System.currentTimeMillis(); this.uuid = uuid; SMART_PLAYERS.put(uuid, this); this.getData(); removeUnneededSmartPlayers(); } public static long getMemoryUsed() { Instrumentation inst = Main.getInstrumentation(); long memory = 0; if (inst != null) { for (SmartPlayer smartPlayer : getSmartPlayers()) { memory += inst.getObjectSize(smartPlayer); } } return memory; } public void getData() { int previousLevel = this.calculateLevel(); Map previousLevels = new HashMap<>(); this.gameExperience.forEach((mainGame, aLong) -> previousLevels.put(mainGame, RelayUtils.calculateLevel(mainGame, aLong))); Main.async(() -> { try { ResultSet resultSet = SmartDatabase.getProfile(this.getUUID()).get(10, TimeUnit.SECONDS); Main.synchronous(() -> this.processResultSet(resultSet)); for (MainGame value : MainGame.values()) { ResultSet stats = SmartDatabase.getStatistics(value, this.getUUID()).get(5, TimeUnit.SECONDS); if (stats.first()) { this.gameExperience.put(value, stats.getLong(ProfileColumn.EXPERIENCE.getName())); //this.gameElo.put(value, stats.getLong("elo")); } else { this.gameExperience.putIfAbsent(value, 0L); //this.gameElo.putIfAbsent(value, 0L); } } Main.synchronous(() -> { if (this.hasReadData && Miscellaneous.enoughTimeHasPassed(this.lastLevelUp, 5000L)) { this.gameExperience.forEach((mainGame, aLong) -> { int level = RelayUtils.calculateLevel(mainGame, aLong); int previous = previousLevels.getOrDefault(mainGame, 0); if (level > 1 && level > previous) { this.levelUp(mainGame); } }); int currentLevel = this.calculateLevel(); if (currentLevel > 1 && currentLevel > previousLevel) { this.levelUp(null); } } else { this.hasReadData = true; } }); } catch (TimeoutException exception) { Main.warn("Time out exception for profile database: (%s) %s", this.getFormattedName(), exception.getMessage()); } catch (Exception exception) { Main.warn("Error occurred while trying to get data for %s: %s", this.getFormattedName(), exception.getMessage()); } }); } public static void onPlayerLeave(PlayerDisconnectEvent event) { getSmartPlayer(event.getPlayer()).onDisconnect(); } public static synchronized SmartPlayer getSmartPlayer(UUID uuid) { return SMART_PLAYERS.get(uuid); } public String getName() { if (this.isOnline()) { return this.proxiedPlayer.getName(); } if (this.previousUsernames.isEmpty() || Miscellaneous.getLastEntry(this.previousUsernames).isEmpty()) { String name = RelayUtils.getName(this.getUUID()); if (name != null) { this.previousUsernames.add(name); return name; } return ""; } return Miscellaneous.getLastEntry(this.previousUsernames); } public void updateProfile(ProfileColumn column) { SmartDatabase.updateProfile(this.getUUID(), column, this.getData(column)); this.broadcastUpdateMessage(); } public void broadcastUpdateMessage() { if (this.isOnline()) { this.sendPluginMessage(Command.UPDATE_INFO, this.getUUID()); } } private Object getData(ProfileColumn column) { switch (column) { case ID: return this.getUUID(); case FIRST_LOGIN: return this.firstJoinTime; case LAST_LOGIN: return this.joinTime; case PREVIOUS_USERNAMES: return toCSV(this.previousUsernames); case GROUPS: return toCSV(this.groups); case FRIENDS: return toCSV(this.friends); case BLOCKED_PLAYERS: return toCSV(this.blockedPlayers); case CHAT_FILTER: return this.chatFilter.toString(); case MESSAGE_FILTER: return this.messageFilter.toString(); case FRIEND_FILTER: return this.friendFilter.toString(); case PARTY_FILTER: return this.partyFilter.toString(); case DUEL_FILTER: return this.duelFilter.toString(); case CREDITS: return this.credits; case IS_NICKED: return this.isNicked ? "1" : "0"; case NICK: return this.nick == null ? "" : this.nick.toString(); case COSMETICS: return toCSV(this.cosmetics); case ACTIVE_COSMETICS: return toCSV(this.activeCosmetics); case GAME_COSMETICS: return GameCosmetic.getJsonArray(this.gameCosmetics); case ACTIVE_GAME_COSMETICS: return GameCosmetic.getJsonArray(this.activeGameCosmetics); case PUNISHMENTS: return Text.escapeSpecialChars(Punishment.getJsonArray(this.punishments).toString()); case EXPERIENCE: return this.xp; case TIME_PLAYED: return this.timePlayed; case EXTRA: return Text.escapeSpecialChars(this.extraData.getData().toString()); case DISCORD_ID: return this.discordId; } return null; } public static synchronized void playerConnectionChange(SmartPlayer smartPlayer, boolean logOn) { Main.doAndLogDuration("PlayerConnectionChange", () -> { if (logOn) { ONLINE_PLAYERS.add(smartPlayer); } else { ONLINE_PLAYERS.remove(smartPlayer); } ComponentBuilder message = new ComponentBuilder(ComponentBuilder.BLUE).append("Friend > ").append(smartPlayer.getComponentFormattedName()).append("%s %s.", YELLOW, logOn ? "joined" : "left"); for (SmartPlayer onlineSmartPlayer : getOnlineSmartPlayers()) { if (onlineSmartPlayer.friends.contains(smartPlayer.getUUID())) { onlineSmartPlayer.sendMessage(message); } } }); } private void onDisconnect() { this.lastDisconnectTime = System.currentTimeMillis(); this.connectingServer = null; playerConnectionChange(this, false); if (this.lastServer != null) { if (!this.lastServer.isHub() || (this.getServer() != null && !this.getServer().isHub())) { this.setReconnectServer(this.lastServer.getHub()); } else if (this.lastServer.isHub()) { this.setReconnectServer(this.lastServer); } this.extraData.setString("lastServer", this.lastServer.getName()); } Server reconnect = Server.getServer(this.proxiedPlayer.getReconnectServer()); if (reconnect == null || !reconnect.isHub()) { this.setReconnectServer(Server.getServerOfType(null, false)); } if (this.isInQueue()) { this.leaveSingular(); } this.hasJoined = false; this.updateProfile(ProfileColumn.EXTRA); Main.schedule(SmartPlayer::checkLimboQueue, 1); } private void onJoin(ProxiedPlayer proxiedPlayer) { this.proxiedPlayer = proxiedPlayer; String name = this.getName(); this.getData(); if (Miscellaneous.enoughTimeHasPassed(this.hasReadData ? this.lastDisconnectTime : 0L, 6, TimeUnit.HOURS)) { Main.schedule(() -> this.sendPluginMessage(Command.OTHER, "dailyjoin", this.getUUID()), 1); } Main.scheduleMillis(() -> { if (Main.networkIsFull(true)) { if (!this.isInGroupOrHigher(Group.CHAMPION)) { Server server = Server.getLimboServer(); if (server == null || !server.isMessageClientActive() || server.getPlayerCount() > 50) { ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.color(ComponentBuilder.YELLOW); componentBuilder.line().newLine(); componentBuilder.color(ComponentBuilder.RED_ORANGE).append("Relay network is currently at maximum capacity.").newLine(); componentBuilder.color(ComponentBuilder.ORANGE_YELLOW).append("Please try reconnecting later."); componentBuilder.color(ComponentBuilder.YELLOW).line(); this.kick(componentBuilder); } else { this.enterLimboQueue(); this.sendToLimbo(); } } else if (this.getHighestGroup() == Group.CHAMPION) { for (SmartPlayer onlineSmartPlayer : getOnlineSmartPlayers()) { if (onlineSmartPlayer.isInLimboQueue()) { onlineSmartPlayer.sendMessage(new ComponentBuilder(this.getComponentFormattedName()).color(ComponentBuilder.GREEN).append(" has skipped the limbo queue with ").color(ComponentBuilder.AQUA).append("Premium ").color(GREEN).append("rank!")); } } } } }, 750); Main.schedule(() -> { if (this.lastName == null || !this.lastName.equals(name)) { Miscellaneous.addIfAbsent(this.previousUsernames, name); this.updateProfile(ProfileColumn.PREVIOUS_USERNAMES); } this.joinTime = System.currentTimeMillis(); this.updateProfile(ProfileColumn.LAST_LOGIN); if (this.isNewLogin) { ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE).newLine(); componentBuilder.color(ComponentBuilder.GREEN).append("Welcome to ").color(ComponentBuilder.AQUA_BLUE).bold().append("Relay!").newLine(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE); if (RelayUtils.testing) { this.addToGroup(Group.CHAMPION); } this.sendMessage(componentBuilder); this.isNewLogin = false; } for (BaseComponent queuedMessage : this.queuedMessages) { this.sendMessage(queuedMessage); } this.queuedMessages.clear(); }, 2); Main.peakPlayers = Math.max(Main.peakPlayers, getOnlineSmartPlayers().size()); playerConnectionChange(this, true); } private void enterLimboQueue() { Miscellaneous.addIfAbsent(LIMBO_QUEUE, this); this.sendLimboQueuePosition(true); } private void processResultSet(ResultSet resultSet) { try { if (resultSet.first()) { long firstLogin = resultSet.getLong(ProfileColumn.FIRST_LOGIN.getName()); long lastLogin = resultSet.getLong(ProfileColumn.LAST_LOGIN.getName()); String previousUsernames = resultSet.getString(ProfileColumn.PREVIOUS_USERNAMES.getName()); String groups = resultSet.getString(ProfileColumn.GROUPS.getName()); String friends = resultSet.getString(ProfileColumn.FRIENDS.getName()); String blockedPlayers = resultSet.getString(ProfileColumn.BLOCKED_PLAYERS.getName()); int chatFilter = resultSet.getInt(ProfileColumn.CHAT_FILTER.getName()); int messageFilter = resultSet.getInt(ProfileColumn.MESSAGE_FILTER.getName()); int friendFilter = resultSet.getInt(ProfileColumn.FRIEND_FILTER.getName()); int partyFilter = resultSet.getInt(ProfileColumn.PARTY_FILTER.getName()); int duelFilter = resultSet.getInt(ProfileColumn.DUEL_FILTER.getName()); long credits = resultSet.getLong(ProfileColumn.CREDITS.getName()); boolean isNicked = resultSet.getBoolean(ProfileColumn.IS_NICKED.getName()); String nickedJson = resultSet.getString(ProfileColumn.NICK.getName()); String cosmetics = resultSet.getString(ProfileColumn.COSMETICS.getName()); String activeCosmetics = resultSet.getString(ProfileColumn.ACTIVE_COSMETICS.getName()); String gameCosmetics = resultSet.getString(ProfileColumn.GAME_COSMETICS.getName()); String activeGameCosmetics = resultSet.getString(ProfileColumn.ACTIVE_GAME_COSMETICS.toString()); String punishments = resultSet.getString(ProfileColumn.PUNISHMENTS.toString()); long xp = resultSet.getLong(ProfileColumn.EXPERIENCE.getName()); long timePlayed = resultSet.getLong(ProfileColumn.TIME_PLAYED.getName()); String extraData = resultSet.getString(ProfileColumn.EXTRA.getName()); long discordId = resultSet.getLong(ProfileColumn.DISCORD_ID.getName()); this.firstJoinTime = firstLogin; this.previousLastJoinTime = lastLogin; if (RelayUtils.INITIALIZATION_TIME - this.previousLastJoinTime > 0 && this.isOnline()) { Main.uniquePlayers++; } this.timePlayed = timePlayed; this.previousUsernames.clear(); boolean updatePreviousUsernames = false; for (String previousUsername : separateCSV(previousUsernames)) { if (!Miscellaneous.addIfAbsent(this.previousUsernames, previousUsername)) { updatePreviousUsernames = true; } } if (updatePreviousUsernames) { this.updateProfile(ProfileColumn.PREVIOUS_USERNAMES); } this.lastName = Miscellaneous.getLastEntry(this.previousUsernames); this.groups.clear(); boolean shouldUpdateGroups = false; for (String permission : separateCSV(groups)) { if (!Miscellaneous.addIfAbsent(this.groups, Group.getGroup(permission))) { shouldUpdateGroups = true; } } if (Miscellaneous.addIfAbsent(this.groups, Group.DEFAULT)) { shouldUpdateGroups = true; } if (shouldUpdateGroups) { this.updateProfile(ProfileColumn.GROUPS); } boolean updateFriends = false; for (String friend : separateCSV(friends)) { if (!Miscellaneous.addIfAbsent(this.friends, UUID.fromString(friend))) { updateFriends = true; } } if (updateFriends) { this.updateProfile(ProfileColumn.FRIENDS); } boolean updateBlocked = false; for (String blockedPlayer : separateCSV(blockedPlayers)) { if (!Miscellaneous.addIfAbsent(this.blockedPlayers, UUID.fromString(blockedPlayer))) { updateBlocked = true; } } if (updateBlocked) { this.updateProfile(ProfileColumn.BLOCKED_PLAYERS); } this.chatFilter = ChatFilter.getChatFilterLevel(chatFilter); this.messageFilter = MessageFilter.getMessageFilter(messageFilter); this.friendFilter = FriendFilter.getFriendFilter(friendFilter); this.partyFilter = PartyFilter.getPartyFilter(partyFilter); this.duelFilter = DuelFilter.getDuelFilter(duelFilter); this.credits = credits; this.isNicked = isNicked; this.nick = Nick.fromString(nickedJson); this.cosmetics.clear(); boolean updateCosmetics = false; for (String cosmetic : separateCSV(cosmetics)) { if (!Miscellaneous.addIfAbsent(this.cosmetics, Cosmetic.getCosmetic(cosmetic))) { updateCosmetics = true; } } if (updateCosmetics) { this.updateProfile(ProfileColumn.COSMETICS); } this.activeCosmetics.clear(); boolean updateActiveCosmetics = false; for (String activeCosmetic : separateCSV(activeCosmetics)) { if (!Miscellaneous.addIfAbsent(this.activeCosmetics, Cosmetic.getCosmetic(activeCosmetic))) { updateActiveCosmetics = true; } } if (updateActiveCosmetics) { this.updateProfile(ProfileColumn.ACTIVE_COSMETICS); } JsonObject gameCosmeticsJson = RelayUtils.parseJsonObject(gameCosmetics); if (gameCosmeticsJson != null) { this.gameCosmetics = GameCosmetic.getMap(gameCosmeticsJson); } JsonObject activeGameCosmeticsJson = RelayUtils.parseJsonObject(activeGameCosmetics); if (activeGameCosmeticsJson != null) { this.activeGameCosmetics = GameCosmetic.getMap(activeGameCosmeticsJson); } JsonArray jsonArray = RelayUtils.parseJsonArray(punishments); if (jsonArray != null) { this.punishments = Punishment.getPunishments(jsonArray); this.punishments.removeIf(Objects::isNull); } this.xp = xp; if (this.isOnline()) { for (UUID friend : this.friends) { getOrCreate(friend); } } for (UUID blockedPlayer : this.blockedPlayers) { getOrCreate(blockedPlayer); } if (this.firstJoinTime < Main.START_OF_2021 && this.isOnline()) { this.firstJoinTime = System.currentTimeMillis(); this.isNewLogin = true; this.updateProfile(ProfileColumn.FIRST_LOGIN); Main.newPlayers++; } JsonObject jsonObject = RelayUtils.parseJsonObject(extraData); if (jsonObject != null) { this.extraData = new ExtraData(jsonObject); this.discordEarned = this.extraData.getInt("discordEarned", this.discordEarned); } this.discordId = discordId; if (!this.hasReadData) { this.manhuntQuits = this.extraData.getInt("manhuntQuits", 0); this.isBannedFromManhunt = this.extraData.getBoolean("manhuntBanned", false); this.checkIfManhuntBanned(false); } } else { String sqlColumns = this.getSQLColumns(); String sql = this.getSQL(); SmartDatabase.addToProfileTable(sqlColumns, sql); this.isNewLogin = true; Main.newPlayers++; Main.uniquePlayers++; } this.onProfileRead(); } catch (Exception exception) { Main.error("Attempted to process result set, error occurred: (%s) %s", this.getFormattedName(), exception.getMessage()); } } public void onProfileRead() { this.checkIfBanned(); BotUtils.onPlayerDataRetrieved(this); if (this.discordEarned > 0) { this.sendDiscordRewardsMessage(); } } public void onJoinServer(ServerInfo target) { if (!this.hasJoined) { Server server = this.lastServer; if (server == null) { server = Server.getServer(this.extraData.getString("lastServer", "hub001")); } if (server != null) { if (!server.isHub()) { server = server.getHub(); } if (server == null || !server.isActive()) { server = Server.getServerOfType(server == null ? null : server.getMainGame(), false); if (server == null || !server.isActive()) { server = Server.getServerOfType(null, false); } } } Server targetServer = Server.getServer(target); if (targetServer != null && targetServer.isLimbo()) { Main.synchronous(this::sendToHub); } else { if (targetServer == null || !targetServer.isHub() || (server != null && targetServer.getMainGame() != server.getMainGame())) { this.setReconnectServer(server); if (server != null && target != server.getServerInfo() && !server.isLimbo()) { Server finalServer = server; Main.synchronous(() -> this.sendToServer(finalServer)); } } } Main.schedule(() -> { if (this.getServer() != null && !this.getServer().isHub() && !this.isInGame()) { this.sendToHub(); } }, 5); } this.hasJoined = true; this.checkIfBanned(); Main.schedule(() -> { this.sendPartyData(); this.sendRequeueData(); }, 1); if (this.isInQueue()) { for (int i = 0; i < 25; i++) { Main.scheduleMillis(() -> { this.hasUpdatedQueueScoreboard = false; this.updateQueueScoreboard(); }, i * 400); } } } private boolean isInGame() { for (GameServer gameServer : GameServer.GAME_SERVERS) { for (GameDetails gameDetail : gameServer.getServerDetails().gameDetails) { if (gameDetail.getPlayers().contains(this.getUUID()) || gameDetail.getSpectators().contains(this.getUUID())) { return true; } } } return false; } public GameServer getGameServer() { for (GameServer gameServer : GameServer.GAME_SERVERS) { for (GameDetails gameDetail : gameServer.getServerDetails().gameDetails) { if (gameDetail.getPlayers().contains(this.getUUID()) || gameDetail.getSpectators().contains(this.getUUID())) { return gameServer; } } } return null; } public void checkIfBanned() { if (this.isOnline()) { if (this.isBanned()) { this.proxiedPlayer.disconnect(this.getBanMessage()); Punishment ipBan = this.getLatestActivePunishment(PunishmentType.IP_BAN); if (ipBan != null) { if (!ipBan.hasExpired() || ipBan.isActive()) { Miscellaneous.addIfAbsent(BANNED_IPS, ipBan.getData()); } else { BANNED_IPS.remove(ipBan.getData()); } } } else if (BANNED_IPS.contains(Utilities.parseAddress(this.proxiedPlayer.getSocketAddress()))) { this.proxiedPlayer.disconnect(new TextComponent(RED + "This IP is currently banned.")); } } } public synchronized void updateScoreboard() { Main.synchronous(() -> { if (this.isOnline()) { if (this.isInQueue()) { this.updateQueueScoreboard(); } else if (this.getServerInfo() == null || this.isOnHub()) { this.sendPluginMessage(Command.SCOREBOARD, this.getUUID()); } } }); } public void updateQueueScoreboard() { int[] queueData = this.getQueueInformation(); int queueSize = -1; int queuePosition = -1; int specificSize = -1; int specificPosition = -1; int typeSize = -1; boolean availableServer = false; if (queueData.length == 7) { queueSize = queueData[0]; queuePosition = queueData[1]; specificSize = queueData[2]; specificPosition = queueData[3]; typeSize = queueData[4]; availableServer = queueData[6] == 1; } this.sendPartyData(); if (this.hasUpdatedQueueScoreboard) { this.sendPluginMessage(Command.OTHER, "queueposition", this.getUUID(), queuePosition, queueSize, specificPosition, specificSize, typeSize, availableServer); } else { this.sendQueueScoreboardMessage(); this.hasUpdatedQueueScoreboard = true; } } public void sendPartyData() { this.sendPluginMessage(Command.OTHER, "partydata", this.getUUID(), this.isInParty() ? this.currentParty.getPartyData() : "none"); } public void sendRequeueData() { if (this.lastGameType != null) { this.sendPluginMessage(Command.OTHER, "requeue", this.getUUID()); } } public void sendQueueScoreboardMessage() { int[] queueData = this.getQueueInformation(); int queueSize = -1; int queuePosition = -1; int specificSize = -1; int specificPosition = -1; int typeSize = -1; boolean fastQueue = false; boolean availableServer = false; if (queueData.length == 7) { queueSize = queueData[0]; queuePosition = queueData[1]; specificSize = queueData[2]; specificPosition = queueData[3]; typeSize = queueData[4]; fastQueue = queueData[5] == 1; availableServer = queueData[6] == 1; } this.sendPluginMessage(Command.OTHER, "queuescoreboard", this.getUUID(), this.currentQueue, queuePosition, queueSize, specificPosition, specificSize, typeSize, fastQueue, availableServer, this.queueData); } public boolean isFastQueue() { return this.isInQueue() && Queue.isInFastQueue(this); } public int getQueuePosition() { if (!this.isInQueue()) { return -1; } return Queue.getQueuePosition(this); } public int getQueueSize() { if (!this.isInQueue()) { return -1; } return Queue.getQueueSize(this); } public int[] getQueueInformation() { if (!this.isInQueue()) { return new int[0]; } return Queue.getQueueInformation(this); } public void sendPacket(DefinedPacket definedPacket) { this.unsafe().sendPacket(definedPacket); } public void test() { ScoreboardObjective objective = new ScoreboardObjective(); objective.setName("test"); objective.setAction((byte) 0); objective.setValue("YouTube"); objective.setType(ScoreboardObjective.HealthDisplay.INTEGER); this.sendPacket(objective); ScoreboardDisplay display = new ScoreboardDisplay(); display.setPosition((byte) 1); display.setName("test"); this.sendPacket(display); ScoreboardScore score = new ScoreboardScore(); score.setItemName("Random"); score.setValue(1); score.setScoreName("test"); score.setAction((byte) 0); this.sendPacket(score); } public Connection.Unsafe unsafe() { return this.proxiedPlayer.unsafe(); } public void setQueue(GameType newQueue) { this.currentQueue = newQueue; this.lastGameType = newQueue; this.hasUpdatedQueueScoreboard = false; } public void emptyQueue() { this.currentQueue = null; this.updateScoreboard(); } public boolean isInQueue() { return this.currentQueue != null; } public boolean isInQueue(GameType gameType) { return this.currentQueue == gameType; } public boolean isInParty() { return this.currentParty != null; } public boolean isPartyOwner() { return this.currentParty.getOwner().equals(this); } public void createParty() { this.currentParty = new Party(this); if (this.isInQueue()) { this.leaveQueue(); } } public boolean isOnline() { return this.proxiedPlayer != null && this.proxiedPlayer.isConnected(); } public boolean isInParty(Party party) { return party.equals(this.currentParty); } public Party getCurrentParty() { return this.currentParty; } public void setLastGame(Server lastGameServer, GameType gameType) { this.lastGameServer = lastGameServer; this.lastGameType = gameType; } public Server getLastGameServer() { return this.lastGameServer; } public void tryRejoin() { if (this.lastGameServer.isMessageClientActive()) { this.lastGameServer.sendMessage(Command.REJOIN, this.getUUID()); } else { this.sendErrorMessage("The game you are trying to rejoin has ended."); } } public SmartPlayer getLastMessageSender() { return this.lastMessageSender; } public void setLastMessageSender(SmartPlayer lastMessageSender) { this.lastMessageSender = lastMessageSender; } public void leaveQueue() { if (this.isInParty()) { if (this.currentParty.canStartGame(this)) { Queue.removePartyFromQueue(this.currentQueue, this, this.currentParty); } else { this.sendPartyErrorMessage("Only party leaders can leave the queue!"); } } else { if (this.currentQueue == null) { Main.error("Attempted to leave queue, is null. (%s)", this.getFormattedName()); return; } Queue.removePlayerFromQueue(this.currentQueue, this); } } public void leaveSingular() { Queue.removePlayerFromQueue(this.currentQueue, this); } public void leaveQueue(GameType gameType) { if (this.isInQueue(gameType)) { this.leaveQueue(); } else { this.sendErrorMessage("You are not in that queue!"); } } public void messagePlayer(SmartPlayer messageRecipient, String message) { if (this.canMessagePlayer(messageRecipient)) { if (SwearUtils.canSendMessageAtLevel(message, messageRecipient.chatFilter)) { ComponentBuilder toStart = new ComponentBuilder(BLUE.toString()).append("To ").append(messageRecipient.getComponentFormattedName()).color(BLUE).append(" > ").color(WHITE); this.sendChatMessage(toStart, this, message); ComponentBuilder fromStart = new ComponentBuilder(RED.toString()).append("From ").append(this.getComponentFormattedName()).color(RED).append(" > ").color(WHITE); messageRecipient.sendChatMessage(fromStart, this, message); messageRecipient.setLastMessageSender(this); this.onSendMessage(message, ChatType.PRIVATE, messageRecipient); } else { this.sendErrorMessage("%s%s chat filter level does not allow your message.", messageRecipient.getPossessiveFormattedName(), RED); } } else { this.sendErrorMessage("You cannot message that player!"); } } private boolean canMessagePlayer(SmartPlayer messageRecipient) { if (messageRecipient.blockedPlayers.contains(this.getUUID())) { return false; } else if (this.blockedPlayers.contains(messageRecipient.getUUID())) { return false; } else if (messageRecipient.messageFilter == MessageFilter.FRIENDS || messageRecipient.messageFilter == MessageFilter.FRIENDS_OF_FRIENDS) { if (messageRecipient.friends.contains(this.getUUID())) { return true; } if (!this.isMuted()) { if (messageRecipient.messageFilter == MessageFilter.FRIENDS_OF_FRIENDS) { for (UUID friend : this.friends) { if (messageRecipient.friends.contains(friend)) { return true; } } } } } else if (messageRecipient.messageFilter == MessageFilter.ANYBODY && !this.isMuted()) { return true; } if (this.isStaff()) { return true; } return false; } public void sendMessage(ComponentBuilder componentBuilder) { this.sendMessage(componentBuilder.getResult()); } public void sendMessage(boolean queueIfOffline, BaseComponent baseComponent) { if (this.isOnline()) { this.proxiedPlayer.sendMessage(baseComponent); } else if (queueIfOffline) { this.queuedMessages.add(baseComponent); } } public void sendMessage(BaseComponent baseComponent) { this.sendMessage(false, baseComponent); } public void sendMessage(boolean queueIfOffline, String format, Object... arguments) { if (this.isOnline()) { this.sendMessage(new TextComponent(RelayUtils.format(format, arguments))); } else if (queueIfOffline) { this.queueMessageForJoin(format, arguments); } } public void sendMessage(String format, Object... arguments) { this.sendMessage(false, format, arguments); } public void sendInfoMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(YELLOW.getColor()).append(format, arguments)); } public void sendErrorMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(RED.getColor()).append(format, arguments)); } public void sendSuccessMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(GREEN.getColor()).append(format, arguments)); } public void sendUsageMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(DARK_GREEN.getColor()).append(format, arguments)); } public void sendPartyInfoMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(ComponentBuilder.RED).append("Party > ").color(YELLOW).append(format, arguments)); } public void sendPartyErrorMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(ComponentBuilder.RED).append("Party > ").color(RED).append(format, arguments)); } public void sendPartySuccessMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(ComponentBuilder.RED).append("Party > ").color(GREEN).append(format, arguments)); } public void sendPartyUsageMessage(String format, Object... arguments) { this.sendMessage(new ComponentBuilder(ComponentBuilder.RED).append("Party > ").color(DARK_GREEN).append(format, arguments)); } public void queueMessageForJoin(String format, Object... data) { this.queueMessageForJoin(new TextComponent(RelayUtils.format(format, data))); } public void queueMessageForJoin(BaseComponent baseComponent) { this.queuedMessages.add(baseComponent); } public void queueInfoMessageForJoin(String format, Object... data) { this.queueMessageForJoin(YELLOW + format, data); } public void queueSuccessMessageForJoin(String format, Object... data) { this.queueMessageForJoin(GREEN + format, data); } public void queueUsageMessageForJoin(String format, Object... data) { this.queueMessageForJoin(DARK_GREEN + format, data); } public void queueErrorMessageForJoin(String format, Object... data) { this.queueMessageForJoin(RED + format, data); } public static void onTick() { if (Main.isSecond()) { checkLimboQueue(); if (Main.isOfSeconds(5)) { if (Main.isOfSeconds(60)) { for (SmartPlayer smartPlayer : getOnlineSmartPlayers()) { smartPlayer.checkForUpdates(); } } for (SmartPlayer smartPlayer : LIMBO_QUEUE) { smartPlayer.sendLimboQueuePosition(false); } } } } public static void checkLimboQueue() { if (!Main.networkIsFull()) { if (!LIMBO_QUEUE.isEmpty()) { SmartPlayer smartPlayer = LIMBO_QUEUE.get(0); LIMBO_QUEUE.remove(0); smartPlayer.sendToHub(); } } } public static SmartPlayer getSmartPlayer(ProxiedPlayer proxiedPlayer) { if (proxiedPlayer == null) return null; SmartPlayer smartPlayer = getSmartPlayer(proxiedPlayer.getUniqueId()); if (smartPlayer == null) { return new SmartPlayer(proxiedPlayer); } return smartPlayer; } public static SmartPlayer getSmartPlayer(CommandSender commandSender) { if (commandSender instanceof ProxiedPlayer) { return getSmartPlayer((ProxiedPlayer) commandSender); } return null; } public static boolean isPlayer(CommandSender commandSender) { return getSmartPlayer(commandSender) != null; } public static SmartPlayer getSmartPlayer(String name) { if (name.length() > 16) { try { return getSmartPlayer(UUID.fromString(name)); } catch (Exception exception) { Main.error("Attempted to parse UUID to get smartplayer: %s", name); return null; } } for (SmartPlayer smartPlayer : getSmartPlayers()) { if (smartPlayer.getName().equalsIgnoreCase(name)) { return smartPlayer; } } return null; } public static SmartPlayer getSmartPlayerOrNick(String name) { SmartPlayer smartPlayer = getSmartPlayer(name); if (smartPlayer == null) { return getFromNickname(name); } return smartPlayer; } public static SmartPlayer getOrCreate(String name) { if (name.length() > 16) { try { UUID uuid = UUID.fromString(name); SmartPlayer smartPlayer = getSmartPlayer(uuid); if (smartPlayer == null) { return new SmartPlayer(uuid); } else { return smartPlayer; } } catch (Exception ignored) { } } else { SmartPlayer smartPlayer = getSmartPlayerOrNick(name); if (smartPlayer == null) { UUID uuid = RelayUtils.getUUID(name); if (uuid != null) { return new SmartPlayer(uuid); } } return smartPlayer; } return null; } public static SmartPlayer getOrCreate(UUID uuid) { if (uuid == null) { return null; } return getOrCreate(String.valueOf(uuid)); } public static Collection getSmartPlayers() { return Miscellaneous.getList(SMART_PLAYERS.values()); } public static Collection getOnlineSmartPlayers() { Collection smartPlayers = getSmartPlayers(); smartPlayers.removeIf(smartPlayer -> !smartPlayer.isOnline()); return smartPlayers; } public static Collection getUUIDs() { return Miscellaneous.getList(SMART_PLAYERS.keySet()); } public static void onPlayerJoin(PostLoginEvent event) { getSmartPlayer(event.getPlayer()).onJoin(event.getPlayer()); } public void sendChatMessage(String start, SmartPlayer sender, String message) { if (!this.blockedPlayers.contains(sender.getUUID())) { String processed = this.processChatMessage(message); if (!processed.isEmpty()) { if (processed.toLowerCase().contains(this.getName().toLowerCase()) || (this.isNicked && processed.toLowerCase().contains(this.getNickname().toLowerCase()))) { this.playSound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP); } this.sendMessage(start + processed); } } } public void sendChatMessage(ComponentBuilder start, SmartPlayer sender, String message) { if (!this.blockedPlayers.contains(sender.getUUID())) { String processed = this.processChatMessage(message); if (!processed.isEmpty()) { if (processed.toLowerCase().contains(this.getName().toLowerCase()) || (this.isNicked && processed.toLowerCase().contains(this.getNickname().toLowerCase()))) { this.playSound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP); } this.sendMessage(start.clone().append(processed)); } } } public String processChatMessage(String message) { String processed = SwearUtils.processChatMessage(message, this.chatFilter); String patternString = "(?i)(" + this.getName() + ")"; if (this.isNicked) { patternString += "|(" + this.getNickname() + ")"; } Pattern pattern = Pattern.compile(patternString); Matcher matcher = pattern.matcher(processed); while (matcher.find()) { String group = matcher.group(); processed = processed.replace(group, YELLOW + group + RESET); } return processed; } public boolean canSendMessage(String message) { if (this.isMuted()) { this.sendErrorMessage("You are currently muted. Your mute expires at %s%s%s.", YELLOW, this.getExpiryDate(this.getLatestActivePunishment(PunishmentType.MUTE)), RED); return false; } if (this.isStaff()) { return true; } boolean canSendChatMessage = SwearUtils.canSendMessageAtLevel(message, this.chatFilter) && !PlayerUtilities.containsWebsiteOrData(message); if (!canSendChatMessage) { this.sendErrorMessage("Your message, '%s%s%s' is not suitable for chat.", YELLOW, message, RED); return false; } if (message.replace("\\s", "").isEmpty()) { this.sendErrorMessage("Please enter a message to send."); return false; } if (!this.lastMessagesMatch(message)) { if (!(this.latestMessagesWithin3Seconds.size() >= 5)) { this.removeLastMessage(); long time = System.currentTimeMillis(); this.lastMessages.put(time, message); Main.schedule(() -> this.lastMessages.remove(time), 5, TimeUnit.MINUTES); this.latestMessagesWithin3Seconds.add(message); Main.schedule(() -> this.latestMessagesWithin3Seconds.remove(message), 3); return true; } else { this.sendErrorMessage("You are sending messages too fast!"); } } else { this.sendErrorMessage("You cannot send the same message twice!"); } return false; } public void onSendMessage(String message, ChatType chatType) { this.onSendMessage(message, chatType, null); } public void onSendMessage(String message, ChatType chatType, SmartPlayer recipient) { ChatMessage chatMessage; if (recipient != null) { chatMessage = new PrivateChatMessage(message, ChatType.PRIVATE, recipient); } else { chatMessage = new ChatMessage(message, chatType); } this.messages.add(chatMessage); } private void removeLastMessage() { if (this.lastMessages.size() >= 5) { this.lastMessages.remove(Miscellaneous.getList(this.lastMessages.keySet()).get(this.lastMessages.size() - 1)); } } private boolean lastMessagesMatch(String message) { if (true) { return false; } for (Map.Entry entry : this.lastMessages.entrySet()) { String previousMessage = entry.getValue(); if ((Miscellaneous.isWithinRange(message.length(), previousMessage.length(), 2) && previousMessage.toLowerCase().contains(message.toLowerCase())) || previousMessage.equalsIgnoreCase(message)) { if (!Miscellaneous.enoughSecondsHavePassed(entry.getKey(), 3)) { return true; } } } return false; } public boolean canBeInvitedToParty(SmartPlayer inviter) { return !(this.eitherAreBlocked(inviter) || !this.canReceivePartyInvite(inviter)); } public boolean inviteToParty(SmartPlayer inviter, boolean isOwner, Party party) { if (this.eitherAreBlocked(inviter) || !this.canReceivePartyInvite(inviter) || this.invitedParties.contains(party)) { return false; } else { this.invitedParties.add(party); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.RED, ComponentBuilder.RED_ORANGE, ComponentBuilder.RED).newLine(); componentBuilder.color(GREEN).append(inviter.getComponentFormattedName()).append(GREEN).append(" has invited you to join %s %sparty! ", isOwner ? "their" : party.getOwner().getPossessiveFormattedName(), GREEN); componentBuilder.command(GOLD + "Click here.", "/party join " + inviter.getName(), RelayUtils.format("%sClick here to join %s%s party.", GREEN, inviter.getPossessiveFormattedName(), GREEN)); componentBuilder.color(YELLOW).append(" You have 60 seconds to accept.").newLine(); componentBuilder.gradientLine(ComponentBuilder.RED, ComponentBuilder.RED_ORANGE, ComponentBuilder.RED); BaseComponent textComponent = componentBuilder.getResult(); ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/party join " + inviter.getName()); textComponent.setClickEvent(clickEvent); String hover = RelayUtils.format("%sClick here to join %s%s party.", GREEN, inviter.getPossessiveFormattedName(), GREEN); List lines = Miscellaneous.getList(hover.split("\n")); HoverEvent hoverEvent = new HoverEvent(net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, Miscellaneous.processList(lines, net.md_5.bungee.api.chat.hover.content.Text::new)); textComponent.setHoverEvent(hoverEvent); this.sendMessage(textComponent); return true; } } public void sendMessage(BaseComponent... baseComponents) { this.proxiedPlayer.sendMessage(baseComponents); } public boolean canReceivePartyInvite(SmartPlayer smartPlayer) { switch (this.partyFilter) { case ANYBODY: return true; case FRIENDS: return this.friends.contains(smartPlayer.getUUID()); case FRIENDS_OF_FRIENDS: return this.hasMutualFriends(smartPlayer); } return !this.isInLimboQueue(); } public void invitePlayerToDuel(SmartPlayer invited, GameType duelType) { if (invited.inviteToDuel(this, duelType)) { this.sendSuccessMessage("Invited %s%s to a %s%s%s. They have 60 seconds to accept.", invited.getFormattedNameOrNick(), GREEN, GOLD, duelType.getFullPrettyName(), GREEN); } else { this.sendErrorMessage("You cannot invite that player to a duel!"); } } public boolean inviteToDuel(SmartPlayer inviter, GameType duelType) { if (this.invitedDuels.containsKey(inviter) || this.eitherAreBlocked(inviter) || !this.canReceiveDuel(inviter)) { return false; } this.invitedDuels.put(inviter, duelType); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.GREEN, ComponentBuilder.YELLOW_GREEN, ComponentBuilder.GREEN).newLine(); componentBuilder.color(GREEN).append(inviter.getComponentFormattedName()).append("%s has invited you to %s%s%s! ", GREEN, GOLD, duelType.getFullPrettyName(), GREEN); componentBuilder.command(GOLD + "Click here.", "/duelaccept " + inviter.getName(), RelayUtils.format("%sClick here to accept %s%s duel request.", AQUA, inviter.getPossessiveFormattedName(), AQUA)); componentBuilder.color(YELLOW).append(" You have 60 seconds to accept.").newLine(); componentBuilder.gradientLine(ComponentBuilder.GREEN, ComponentBuilder.YELLOW_GREEN, ComponentBuilder.GREEN); BaseComponent textComponent = componentBuilder.getResult(); ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/duelaccept " + inviter.getName()); textComponent.setClickEvent(clickEvent); String hover = RelayUtils.format("%sClick here to accept %s%s duel request.", AQUA, inviter.getPossessiveFormattedName(), AQUA); List lines = Miscellaneous.getList(hover.split("\n")); HoverEvent hoverEvent = new HoverEvent(net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, Miscellaneous.processList(lines, net.md_5.bungee.api.chat.hover.content.Text::new)); textComponent.setHoverEvent(hoverEvent); this.sendMessage(textComponent); Main.schedule(() -> { if (this.invitedDuels.containsKey(this)) { this.invitedDuels.remove(this); inviter.sendInfoMessage("The duels invite to %s%s has expired.", this.getFormattedNameOrNick(), YELLOW); this.sendInfoMessage("The duels invite from %s%s has expired.", inviter.getFormattedName(), YELLOW); } }, 60); return true; } public boolean canReceiveDuel(SmartPlayer smartPlayer) { switch (this.duelFilter) { case ANYBODY: return true; case FRIENDS: return this.friends.contains(smartPlayer.getUUID()); case FRIENDS_OF_FRIENDS: return this.hasMutualFriends(smartPlayer); } return false; } public void invitePlayerToManhunt(SmartPlayer invited) { if (invited.inviteToManhunt(this)) { this.sendSuccessMessage("Invited %s%s to a %s%s%s. They have 60 seconds to accept.", invited.getFormattedName(), GREEN, GOLD, GameType.MANHUNT_1V1.getFullPrettyName(), GREEN); } else { this.sendErrorMessage("You cannot invite that player to a duel!"); } } public boolean inviteToManhunt(SmartPlayer inviter) { if (this.invitedDuels.containsKey(inviter) || this.eitherAreBlocked(inviter) || !this.canReceiveDuel(inviter)) { return false; } this.invitedDuels.put(inviter, GameType.MANHUNT_1V1); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.GREEN, ComponentBuilder.YELLOW_GREEN, ComponentBuilder.GREEN).newLine(); componentBuilder.color(GREEN).append(inviter.getComponentFormattedName()).append("%s has invited you to %s%s%s! ", GREEN, GOLD, GameType.MANHUNT_1V1.getFullPrettyName(), GREEN); componentBuilder.command(GOLD + "Click here.", "/manhuntaccept " + inviter.getName(), RelayUtils.format("%sClick here to accept %s%s manhunt request.", AQUA, inviter.getPossessiveFormattedName(), AQUA)); componentBuilder.color(YELLOW).append(" You have 60 seconds to accept.").newLine(); componentBuilder.gradientLine(ComponentBuilder.GREEN, ComponentBuilder.YELLOW_GREEN, ComponentBuilder.GREEN); this.sendMessage(componentBuilder); Main.schedule(() -> { if (this.invitedDuels.containsKey(this)) { this.invitedDuels.remove(this); inviter.sendInfoMessage("The manhunt invite to %s%s has expired.", this.getFormattedName(), YELLOW); this.sendInfoMessage("The manhunt invite from %s%s has expired.", inviter.getFormattedName(), YELLOW); } }, 60); return true; } public boolean hasMutualFriends(SmartPlayer smartPlayer) { if (this.friends.contains(smartPlayer.getUUID())) return true; for (UUID friend : this.friends) { if (smartPlayer.friends.contains(friend)) return true; } return false; } public boolean eitherAreBlocked(SmartPlayer other) { return this.blockedPlayers.contains(other.getUUID()) || other.blockedPlayers.contains(this.getUUID()); } public void tryAcceptDuelRequest(SmartPlayer toAccept) { if (this.invitedDuels.containsKey(toAccept)) { if (!toAccept.isOnline()) { this.invitedDuels.remove(toAccept); this.sendErrorMessage("That player is not online!"); } else { GameType gameType = this.invitedDuels.remove(toAccept); DuelAcceptCommand.doDuel(toAccept, this, gameType); } } else { this.sendErrorMessage("That player has not invited you to a duel!"); } } public List getPartiesToJoin() { List parties = new ArrayList<>(); for (Party party : this.invitedParties) { parties.add(party.getOwner().getName()); } return parties; } public void addFriend(String playerName) { SmartPlayer smartPlayer = SmartPlayer.getSmartPlayer(playerName); if (this.friends.size() == 100) { this.sendErrorMessage("You have reached the maximum of 100 friends!"); return; } if (smartPlayer == null) { if (isNickname(playerName)) { this.sendErrorMessage("You cannot send a friend request to that player!"); } else { this.noPlayerFound(playerName); } } else if (!smartPlayer.isOnline()) { this.sendErrorMessage("That player is not online!"); } else if (smartPlayer == this) { this.sendErrorMessage("You cannot friend yourself!"); } else { UUID playerUUID = smartPlayer.getUUID(); if (this.friends.contains(playerUUID)) { this.sendErrorMessage("You already have %s%s friended!", smartPlayer.getFormattedName(), RED); } else { if (smartPlayer.receiveFriendRequest(this)) { this.sendSuccessMessage("Sent a friend request to %s%s. They have 60 seconds to accept.", smartPlayer.getFormattedName(), GREEN); } else { this.sendErrorMessage("You cannot send a friend request to that player!"); } } } } public boolean receiveFriendRequest(SmartPlayer requester) { if (this.receivedFriendRequests.contains(requester) || this.eitherAreBlocked(requester) || !this.canReceiveFriendRequest(requester)) { return false; } this.receivedFriendRequests.add(requester); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE).newLine(); componentBuilder.color(GREEN).append(requester.getFormattedName()).append(GREEN).append(" sent you a friend request! "); componentBuilder.command(GOLD + "Click here.", "/friend accept " + requester.getName(), RelayUtils.format("%sClick here to accept %s%s friend request.", GREEN, requester.getPossessiveFormattedName(), GREEN)); componentBuilder.color(YELLOW).append(" You have 60 seconds to accept.").newLine(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE); this.sendMessage(componentBuilder); Main.schedule(() -> { if (this.receivedFriendRequests.remove(requester)) { requester.sendInfoMessage("The friend request to %s%s has expired.", this.getFormattedName(), YELLOW); this.sendInfoMessage("The duels invite from %s%s has expired.", requester.getFormattedName(), YELLOW); } }, 60); return true; } public boolean canReceiveFriendRequest(SmartPlayer smartPlayer) { switch (this.friendFilter) { case ANYBODY: return true; case FRIENDS_OF_FRIENDS: return this.hasMutualFriends(smartPlayer); } return false; } public void unfriendPlayer(String playerName) { SmartPlayer smartPlayer = SmartPlayer.getSmartPlayer(playerName); if (smartPlayer == null) { if (isNickname(playerName)) { this.sendErrorMessage("You do not have that player friended!"); } else { this.noPlayerFound(playerName); } } else if (smartPlayer == this) { this.sendErrorMessage("You cannot unfriend yourself!"); } else { UUID playerUUID = smartPlayer.getUUID(); if (this.friends.remove(playerUUID)) { smartPlayer.friends.remove(this.getUUID()); smartPlayer.sendInfoMessage("%s%s removed you from their friend list!", this.getFormattedName(), YELLOW); this.sendSuccessMessage("Removed %s%s from your friend list.", smartPlayer.getFormattedName(), GREEN); smartPlayer.updateProfile(ProfileColumn.FRIENDS); this.updateProfile(ProfileColumn.FRIENDS); } else { this.sendErrorMessage("You do not have that player friended!"); } } } public boolean hasOnlineFriend() { for (UUID friend : this.friends) { SmartPlayer smartPlayer = getSmartPlayer(friend); if (smartPlayer != null && smartPlayer.isOnline()) { return true; } } return false; } public void listFriends(int page) { if (this.friends.isEmpty()) { this.sendInfoMessage("Your friends list is empty."); } else { List orderedFriendList = this.getOrderedFriendList(); if (orderedFriendList.size() - ((page - 1) * 8) > 0) { ComponentBuilder componentBuilder = new ComponentBuilder(); String baseCommand = "/friend list "; if (page > 1) { componentBuilder.command(GOLD + "<<", baseCommand + (page - 1), AQUA + "Click here to view the previous page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.append(" "); componentBuilder.gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.BLUE, ComponentBuilder.AQUA).append(" %s[%d] ", GOLD, page).gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.AQUA, ComponentBuilder.BLUE).append(" "); if (orderedFriendList.size() - (page * 8) > 0) { componentBuilder.command(GOLD + ">>", baseCommand + (page + 1), AQUA + "Click here to view the next page."); } else { componentBuilder.append(GOLD + "||"); } for (int i = 8 * (page - 1) + 1; i < 8 * page; i++) { int x = orderedFriendList.size() - i; if (x == -1) { break; } componentBuilder.newLine(); componentBuilder.append(getPlayerInfo(orderedFriendList.get(x))); } componentBuilder.newLine(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA, ComponentBuilder.BLUE); this.sendMessage(componentBuilder); } else { this.sendErrorMessage("The page number %s%d%s is out of bounds.", YELLOW, page, RED); } } } public static BaseComponent getPlayerInfo(UUID uuid) { SmartPlayer smartPlayer = getOrCreate(uuid); if (smartPlayer == null) { Main.error("Could not find smart player for player info: %s", uuid); return new ComponentBuilder().getResult(); } return smartPlayer.getFriendListInfo(); } public BaseComponent getFriendListInfo() { if (this.isOnline()) { return this.getLocationForInfo(); } return new ComponentBuilder(this.getComponentFormattedNameColor()).append(this.getName()).color(RED).append(" is offline").getResult(); } public List getOrderedFriendList() { List offline = new ArrayList<>(this.friends); List online = new ArrayList<>(); for (UUID uuid : offline) { SmartPlayer smartPlayer = getSmartPlayer(uuid); if (smartPlayer != null && smartPlayer.isOnline()) { online.add(uuid); } } offline.removeAll(online); List result = sortByValue(PlayerUtilities.getUUIDToNameMapFromUUIDs(offline)); result.addAll(sortByValue(PlayerUtilities.getUUIDToNameMapFromUUIDs(online))); return result; } private static List sortByValue(Map unsortMap) { List> list = new LinkedList<>(unsortMap.entrySet()); Collections.sort(list, new Comparator() { @SuppressWarnings("unchecked") public int compare(Object o1, Object o2) { boolean o1Null = o1 == null || ((Map.Entry) o1).getValue() == null; boolean o2Null = o2 == null || ((Map.Entry) o2).getValue() == null; if (o1Null && o2Null) return 0; if (o1Null) return -1; if (o2Null) return 1; V a = ((Map.Entry) o1).getValue(); V b = ((Map.Entry) o2).getValue(); return ((Comparable) a).compareTo(b); } }); Map result = new LinkedHashMap<>(); for (Map.Entry entry : list) { result.put(entry.getKey(), entry.getValue()); } return new ArrayList<>(result.keySet()); } public void tryAcceptFriendRequest(String playerName) { SmartPlayer smartPlayer = SmartPlayer.getSmartPlayer(playerName); if (smartPlayer == null) { this.noPlayerFound(playerName); } else { if (this.receivedFriendRequests.remove(smartPlayer)) { this.acceptFriend(smartPlayer); smartPlayer.acceptFriend(this); } else { this.sendErrorMessage("That player has not sent you a friend request!"); } } } public void acceptFriend(SmartPlayer smartPlayer) { this.friends.add(smartPlayer.getUUID()); this.sendSuccessMessage("You are now friends with %s%s.", smartPlayer.getFormattedName(), GREEN); this.updateProfile(ProfileColumn.FRIENDS); } public void blockPlayer(String playerName) { SmartPlayer smartPlayer = SmartPlayer.getSmartPlayer(playerName); if (this.blockedPlayers.size() == 100) { this.sendErrorMessage("You have reached the maximum of 100 blocked players!"); return; } if (smartPlayer == null) { if (isNickname(playerName)) { smartPlayer = getFromNickname(playerName); UUID playerUUID = smartPlayer.getUUID(); if (this.blockedPlayers.contains(playerUUID)) { this.sendErrorMessage("You already have already blocked %s%s.", smartPlayer.getFormattedNickname(), RED); } else { this.blockedPlayers.add(playerUUID); this.sendSuccessMessage("Blocked %s%s.", smartPlayer.getFormattedNickname(), GREEN); this.updateProfile(ProfileColumn.BLOCKED_PLAYERS); } } else { this.noPlayerFound(playerName); } } else if (smartPlayer == this) { this.sendErrorMessage("You cannot block yourself!"); } else { UUID playerUUID = smartPlayer.getUUID(); if (this.blockedPlayers.contains(playerUUID)) { this.sendErrorMessage("You already have already blocked %s%s.", smartPlayer.getFormattedName(), RED); } else { this.blockedPlayers.add(playerUUID); this.sendSuccessMessage("Blocked %s%s.", smartPlayer.getFormattedName(), GREEN); this.updateProfile(ProfileColumn.BLOCKED_PLAYERS); } } } public void unblockPlayer(String playerName) { SmartPlayer smartPlayer = SmartPlayer.getSmartPlayer(playerName); if (smartPlayer == null) { if (isNickname(playerName)) { this.sendErrorMessage("You do not have %s%s blocked.", getActualNickname(playerName), RED); } else { this.noPlayerFound(playerName); } } else if (smartPlayer == this) { smartPlayer.sendErrorMessage("You cannot unblock yourself!"); } else { UUID playerUUID = smartPlayer.getUUID(); if (this.blockedPlayers.remove(playerUUID)) { this.sendSuccessMessage("Unblocked %s%s.", smartPlayer.getFormattedName(), GREEN); this.updateProfile(ProfileColumn.BLOCKED_PLAYERS); } else { this.sendErrorMessage("You do not have %s%s blocked.", smartPlayer.getFormattedName(), RED); } } } public void listBlockedPlayers(int page) { if (this.blockedPlayers.isEmpty()) { this.sendInfoMessage("Your block list is empty."); } else { List orderedUUIDs = new ArrayList<>(sortByValue(PlayerUtilities.getUUIDToNameMapFromUUIDs(this.blockedPlayers))); if (orderedUUIDs.size() - ((page - 1) * 8) > 0) { ComponentBuilder componentBuilder = new ComponentBuilder(); String baseCommand = "/block list "; if (page > 1) { componentBuilder.command(GOLD + "<<", baseCommand + (page - 1), AQUA + "Click here to view the previous page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.append(" "); componentBuilder.gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.RED, ComponentBuilder.ORANGE).append(" %s[%d] ", GOLD, page).gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.ORANGE, ComponentBuilder.RED).append(" "); if (orderedUUIDs.size() - (page * 8) > 0) { componentBuilder.command(GOLD + ">>", baseCommand + (page + 1), AQUA + "Click here to view the next page."); } else { componentBuilder.append(GOLD + "||"); } for (int i = 8 * (page - 1) + 1; i < 8 * page; i++) { int x = orderedUUIDs.size() - i; if (x == -1) { break; } componentBuilder.newLine().color(YELLOW); componentBuilder.append(getOrCreate(orderedUUIDs.get(x)).getName()); } componentBuilder.newLine(); componentBuilder.gradientLine(ComponentBuilder.RED, ComponentBuilder.ORANGE, ComponentBuilder.RED); this.sendMessage(componentBuilder); } else { this.sendErrorMessage("The page number %s%d%s is out of bounds.", YELLOW, page, RED); } } } public ProxiedPlayer getProxiedPlayer() { return this.proxiedPlayer; } public boolean setChatType(ChatType chatType) { switch (chatType) { case ALL: if (this.chatType == ChatType.ALL) { this.sendErrorMessage("You are already using all chat!"); } else { this.chatType = ChatType.ALL; this.sendSuccessMessage("You are now using all chat."); } break; case PARTY: if (this.chatType == ChatType.PARTY) { this.sendErrorMessage("You are already using party chat!"); } else { if (this.isInParty()) { this.chatType = ChatType.PARTY; this.sendSuccessMessage("You are now using party chat."); } else { this.sendErrorMessage("You are not in a party!"); } } break; case STAFF: if (this.isStaff()) { if (this.chatType == ChatType.STAFF) { this.sendErrorMessage("You are already using staff chat!"); } else { this.chatType = ChatType.STAFF; this.sendSuccessMessage("You are now using staff chat."); } } else { return false; } break; } return true; } public boolean cannotSendCommand() { if (this.isStaff()) return false; if (this.commandsInPast5Seconds <= 3) { this.commandsInPast5Seconds++; Main.schedule(() -> this.commandsInPast5Seconds--, 5); return false; } return true; } public String getFormattedName() { Group highest = this.getHighestGroup(); if (highest == null) { return Group.DEFAULT.formatName(this.getName(), this.getChampionTier()); } return highest.formatName(this.getName(), this.getChampionTier()); } public BaseComponent getComponentFormattedName() { Group highest = this.getHighestGroup(); if (highest == null) { return Group.DEFAULT.formatComponentName(this.getName(), this.getChampionTier()); } return highest.formatComponentName(this.getName(), this.getChampionTier()); } public BaseComponent getComponentFormattedNameOrNick() { if (this.isAndHasNick()) { Group highest = this.nick.group; return highest.formatComponentName(this.nick.name, 0); } return this.getComponentFormattedName(); } public String getPossessiveFormattedName() { return com.lifeknight.relayutils.basic.Text.getPossessiveName(this.getFormattedName()); } public String getFormattedNickname() { return this.nick.getFormattedName(); } public ChatColor getFormattedNameColor() { return this.getHighestGroup().getColor(); } public ChatColor getFormattedNickColor() { return this.nick.group.getColor(); } public Group getHighestGroup() { return Group.getHighestGroup(this.groups); } public String getServerName() { return this.getServerInfo().getName(); } public boolean isOnServer(String serverName) { return this.getServerName().equalsIgnoreCase(serverName); } public boolean isOnSameServer(SmartPlayer smartPlayer) { return this.isOnline() && this.getServerInfo() == (smartPlayer.getServerInfo()); } public void queueGame(GameType gameType) { if (gameType.getTotalPlayerCount() == 1) { this.queueGame(gameType, new QueueData().setParty()); } else { this.queueGame(gameType, new QueueData()); } } public void queueGame(GameType gameType, QueueData queueData) { if (gameType == null) return; if (!gameType.isEnabled()) { this.sendErrorMessage("%s%s%s is currently disabled. Visit our Discord server for more information.", YELLOW, gameType.getFullPrettyName(), RED); return; } if (gameType == GameType.TERMINATOR_SOLOS) { if (!this.hasChampion()) { this.sendChampionPurchaseMessage(); return; } } if (gameType.isExplicitParty()) { queueData.setParty(); } if (queueData.isParty() && gameType.getTotalPlayerCount() > 1) { if (!this.isInParty() || this.currentParty.getSize() < 2) { queueData.attachBoolean("party", false); } } if (!Main.queueOpen) { ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.color(ComponentBuilder.YELLOW).line().newLine(); componentBuilder.color(RED).append("Game queues are currently closed. Message from Relay:").newLine(); componentBuilder.color(WHITE).append(Main.queueCloseMessage == null ? "None" : Main.queueCloseMessage).newLine(); componentBuilder.color(ComponentBuilder.YELLOW).line(); this.sendMessage(componentBuilder); return; } if (gameType.isExplicitParty()) { if (!this.isInParty()) { this.sendErrorMessage("This game can only be played in parties of 2 or more!"); } else { if (this.currentParty.canStartGame(this)) { if (this.currentParty.getSize() < 2) { this.sendErrorMessage("You can only play party games with party sizes of 2 or more!"); } else { if (this.currentParty.hasOfflinePlayers()) { this.sendPartyErrorMessage("You cannot join a queue while your party has players offline!"); } else { if (gameType.getMainGame() == MainGame.MANHUNT) { if (this.isBannedFromManhunt) { this.sendErrorMessage("You are currently banned from Manhunt for quitting before the game ends!"); this.sendManhuntBanTimeRemaining(); } else { if (this.currentParty.getSize() > 16 && !this.isAdministrator()) { this.sendErrorMessage("You can play Manhunt with a max of 16 players!"); } else { this.sendQueue(gameType, queueData); } } } else { this.sendQueue(gameType, queueData); } } } } else { this.sendErrorMessage("Only party leaders can join games!"); } } } else { if (this.isInParty()) { if (!this.getCurrentParty().canStartGame(this)) { this.sendPartyErrorMessage("Only party leaders can join games!"); } else if (this.currentParty.hasOfflinePlayers()) { this.sendPartyErrorMessage("You cannot join a queue while your party has players offline!"); } else { /* if (gameType.isRankedType() && this.currentParty.getSize() > gameType.getTeamSize()) { boolean single = gameType.getTeamSize() == 1; if (single) { this.sendErrorMessage("This game cannot be played in a party!"); } else { this.sendErrorMessage("This game can be played with maximum party size of %s%d%s!", YELLOW, RED); } } else { */ if (gameType.getMainGame() == MainGame.MANHUNT) { if (this.isBannedFromManhunt) { this.sendErrorMessage("You are currently banned from Manhunt for quiting before the game ends!"); this.sendManhuntBanTimeRemaining(); } else { for (SmartPlayer player : this.currentParty.getPlayers()) { if (player.isBannedFromManhunt) { this.sendErrorMessage("One of the players in your party is banned from Manhunt for quitting games!"); player.sendErrorMessage("Your party leader could not queue because you are banned from Manhunt."); player.sendManhuntBanTimeRemaining(); return; } } this.sendQueue(gameType, queueData); } } else { this.sendQueue(gameType, queueData); } // } } } else { if (gameType.getMainGame() == MainGame.MANHUNT && this.isBannedFromManhunt) { this.sendErrorMessage("You are currently banned from Manhunt for quiting before the game ends!"); } else { this.sendQueue(gameType, queueData); } } } } private void sendManhuntBanTimeRemaining() { this.sendInfoMessage("Time remaining on your Manhunt ban: %s%s", RED, this.getFormattedManhuntBanRemainingTime()); } private void sendQueue(GameType gameType, QueueData queueData) { if (this.isInParty()) { if (gameType == GameType.DUELS_CUSTOM) { queueData.attachData("owner", this.getUUID()); } Queue.addPartyToQueue(gameType, queueData, this, this.currentParty); } else { Queue.addPlayerToQueue(gameType, queueData, this); } this.queueData = queueData; } public boolean removeFromGroup(Group group) { if (this.groups.remove(group)) { this.groupUpdateCheck(); this.updateProfile(ProfileColumn.GROUPS); return true; } return false; } public void groupUpdateCheck() { if (!this.isInGroupOrHigher(Group.TIERIII) && this.isAndHasNick()) { this.isNicked = false; this.onNickChange(); } } public boolean addToGroup(Group group) { if (Miscellaneous.addIfAbsent(this.groups, group)) { this.updateProfile(ProfileColumn.GROUPS); return true; } return false; } public synchronized boolean isInGroup(Group group) { return this.groups.contains(group); } public boolean isStaff() { return this.isInGroupOrHigher(Group.DEVELOPER); } public boolean hasChampion() { return this.isInGroupOrHigher(Group.CHAMPION); } public boolean hasMedia() { return this.isInGroupOrHigher(Group.TIKTOK); } public boolean isInGroupOrHigher(Group group) { if (this.isInGroup(group)) return true; return this.isInHigherGroup(group); } public synchronized boolean isInHigherGroup(Group group) { for (Group group1 : this.groups) { if (group1.isHigherThan(group)) { return true; } } return false; } public boolean isAdministrator() { return this.isInGroupOrHigher(Group.ADMIN); } public void sendInsufficientPermissionsMessage() { CommandUtilities.sendInsufficientPermissionsMessage(this.proxiedPlayer); } public void sendCommandsMessage(List commands) { CommandUtilities.sendCommandsMessage(this.proxiedPlayer, commands); } public void noPlayerFound(String name) { CommandUtilities.noPlayerFound(this.proxiedPlayer, name); } public void sendToPlayer(SmartPlayer smartPlayer) { this.sendToServer(smartPlayer.getServerInfo()); } public void sendToPlayer(ProxiedPlayer proxiedPlayer) { this.sendToServer(proxiedPlayer.getServer().getInfo()); } public synchronized void sendToServer(ServerInfo serverInfo) { if (serverInfo != null) { this.lastServer = Server.getServer(serverInfo); if (this.getServerInfo() != serverInfo) { if (this.connectingServer != serverInfo || Miscellaneous.enoughSecondsHavePassed(lastConnectTime, 1)) { this.connectingServer = serverInfo; this.lastConnectTime = System.currentTimeMillis(); Main.synchronous(() -> this.proxiedPlayer.connect(serverInfo)); RelayUtils.log("Connecting %s to %s", this.getName(), serverInfo.getName()); } } } else { Main.error("Could not send to null serverinfo: %s", this.getName()); } } public void sendToServer(Server server) { if (server != null) { if (server.isLimbo()) { this.lastHubServer = this.getServer(); } /* if (!this.isInGroupOrHigher(Group.CHAMPION) && server.isHub() && !server.hasSufficientSpace()) { if (!this.getServer().isHub()) { this.sendToLimbo(); this.sendErrorMessage("The server you are trying to connect to is full. Sending you to limbo."); } else { this.sendErrorMessage("The server you are trying to connect to is full."); } } else { */ this.sendToServer(server.getServerInfo()); // } } else { Main.error("Could not connect %s to null server.", this.getName()); BotUtils.onNullServer(); this.sendErrorMessage("The server you are trying to connect to is currently restarting. Try again later."); } } public void sendToServer(String serverName) { if (Text.comparablyEquals(serverName, "limbo")) { this.sendToServer(Server.getLimboServer()); Main.schedule(() -> this.sendPluginMessage(Command.OTHER, "limbo", this.getUUID()), 1); return; } if (Text.comparablyEquals(serverName, "afk")) { this.sendToServer(Server.getLimboServer()); } Server server = Server.getServer(serverName); if (server == null) { try { this.sendToServer(PlayerUtilities.getHubServer(serverName)); } catch (Exception e) { this.sendToServer(Server.getServerOfType(null, false)); } } else { this.sendToServer(Server.getServer(serverName)); } } public void sendToServer(MainGame mainGame) { if (this.lastHubServer != null && this.lastHubServer.getMainGame() == mainGame && this.lastHubServer.isViable(1)) { this.sendToServer(this.lastHubServer); } else { Server server = Server.getServerOfType(mainGame, false); if (server == null) { this.sendErrorMessage("The lobby you are trying to connect to is currently down. Please try again later. Contact an administrator if this issue persists."); } else if (server.getMainGame() != mainGame) { if (server == this.getServer()) { this.sendErrorMessage("The lobby you are trying to connect to is restarting. Use the %s/queue %scommand if needed. Contact an administrator if this issue persists.", YELLOW, RED); } else { this.sendErrorMessage("The lobby you are trying to connect to is currently restarting. Sending you to the main hub. Contact an administrator if this issue persists."); } } this.sendToServer(server); } } public void spectatePlayer(SmartPlayer toSpectate) { ProxiedPlayer toSpectateProxiedPlayer = toSpectate.getProxiedPlayer(); if (toSpectate.isOnHub()) { this.sendToPlayer(toSpectateProxiedPlayer); } else { Server server = toSpectate.getServer(); if (server.isMessageClientActive()) { server.sendMessage(Command.SPECTATE, this.getUUID(), toSpectate.getUUID()); } else { this.sendErrorMessage("An error occurred while attempting to spectate that player. Please try again in 30 seconds."); } } } public UUID getUUID() { return this.isOnline() ? this.proxiedPlayer.getUniqueId() : this.uuid; } public void updateReconnectServer(ServerInfo serverInfo) { Server server = Server.getServer(serverInfo); try { this.setReconnectServer(server.getHub()); } catch (Exception exception) { Main.error("An error occurred while attempting to change the reconnect server for %s: %s", this.getFormattedName(), exception.getMessage()); } } public void setReconnectServer(ServerInfo serverInfo) { this.proxiedPlayer.setReconnectServer(serverInfo); } public void setReconnectServer(Server server) { if (server != null) { this.setReconnectServer(server.getServerInfo()); } } public String getSQL() { StringBuilder stringBuilder = new StringBuilder("("); ProfileColumn[] columns = ProfileColumn.values(); for (int i = 0; i < columns.length; i++) { Object data = this.getData(columns[i]); data = "'" + data + "'"; stringBuilder.append(data); if (i != columns.length - 1) { stringBuilder.append(", "); } } return stringBuilder.append(")").toString(); } public String getSQLColumns() { StringBuilder stringBuilder = new StringBuilder("("); String[] columnNames = toStringList(ProfileColumn.values()).toArray(new String[0]); for (int i = 0; i < columnNames.length; i++) { String columnName = columnNames[i]; stringBuilder.append("`").append(columnName).append("`"); if (i != columnNames.length - 1) { stringBuilder.append(", "); } } return stringBuilder.append(")").toString(); } public ServerInfo getServerInfo() { if (!this.isOnline()) { return null; } net.md_5.bungee.api.connection.Server server = this.proxiedPlayer.getServer(); if (server == null) { return null; } return server.getInfo(); } public List getNamesOfPlayersOnServer() { List names = Miscellaneous.processList(this.getServerInfo().getPlayers(), proxiedPlayer1 -> getSmartPlayer(proxiedPlayer1).getNameOrNick()); names.remove(this.getNameOrNick()); return names; } public BaseComponent getLocationForInfo() { ComponentBuilder base = new ComponentBuilder(this.getComponentFormattedNameColor()).append(this.getName()).append(YELLOW).append(" "); if (this.isOnHub()) { base.append("is in the ").append(this.getServer().getNameForInfo()).append(" lobby"); } else if (this.getServer().isLimbo()) { base.append("is ").append(this.isInLimboQueue() ? "waiting in Limbo" : "AFK"); } else { base.append("is in a ").append(this.lastGameType == null ? this.getServer().getNameForInfo() : this.lastGameType.getFullPrettyName()).append(" game"); } return base.getResult(); } private Color getComponentFormattedNameColor() { if (this.getHighestGroup() == Group.CHAMPION) { return RelayUtils.getChampionTierColor(this.getChampionTier()); } return this.getHighestGroup().getOther(); } public boolean isOnHub() { return this.getServer() == null || this.getServer().isHub(); } public Server getServer() { return Server.getServer(this.getServerInfo()); } public boolean hasNick() { return this.nick != null; } public boolean isAndHasNick() { return this.isInGroupOrHigher(Group.CHAMPION) && this.isNicked && this.hasNick(); } public void setNick(Nick nick) { this.isNicked = true; this.nick = nick; this.sendSuccessMessage("Your nick:"); this.sendInfoMessage("Name: %s", nick.getFormattedName()); if (nick.textures != null) { this.sendInfoMessage("Skin: %s%s", ChatColor.GREEN, nick.texturesOwnerName); } this.onNickChange(); } public void onNickChange() { this.proxiedPlayer.setDisplayName(this.getName()); this.updateProfile(ProfileColumn.NICK); this.updateProfile(ProfileColumn.IS_NICKED); } public boolean canNick() { if (GameServer.findRejoin(this.getUUID()) != null) { this.sendErrorMessage("You can not rejoin while you can rejoin a match!"); return false; } if (!this.getHighestGroup().isHigherThan(Group.CHAMPION)) { if (this.hasNick()) { Nick nick = this.nick; long timeNeededToElapse = (this.getHighestGroup() == Group.CHAMPION ? 5 : 60) * 60 * 1000L; long timeRemaining = nick.time + timeNeededToElapse - System.currentTimeMillis(); if (timeRemaining > 0) { if (this.isInGroup(Group.CHAMPION)) { this.sendErrorMessage("You must wait 5 minutes before changing your nickname (%s)!", formatTimeFromMilliseconds(timeRemaining)); } else { this.sendErrorMessage("You must wait 6 hours before changing your nickname (%s)! Upgrade to Premium rank to wait 5 minutes!", formatTimeFromMilliseconds(timeRemaining)); } return false; } } } if (this.isStaff()) { return true; } if (!this.isOnHub()) { this.sendErrorMessage("You can only nick in lobbies!"); return false; } return true; } public String getFormattedManhuntBanRemainingTime() { return formatTimeFromMilliseconds(this.extraData.getLong("manhuntBanStart", 0) + this.getManhuntBanDuration() - System.currentTimeMillis()); } public void manhuntQuit() { if (this.isStaff()) return; this.manhuntQuits++; if (this.manhuntQuits == 1) { this.sendMessage(true, "%sIf you continue to abandon Manhunt games, you will be banned from the queue.", RED); } this.checkIfManhuntBanned(true); } public void checkIfManhuntBanned(boolean increase) { if (!this.isBannedFromManhunt) { if (increase && this.manhuntQuits > 1) { this.sendMessage(true, "%sFor abandoning Manhunt games, you have been banned from the Manhunt public queue for %d hour%s!", RED, this.getManhuntBanHours(), this.getManhuntBanHours() == 1 ? "" : "s"); this.isBannedFromManhunt = true; this.extraData.setNumber("manhuntBanStart", System.currentTimeMillis()); this.extraData.setBoolean("manhuntBanned", this.isBannedFromManhunt); return; } if (this.manhuntQuits > 0 && Miscellaneous.enoughTimeHasPassed(this.extraData.getLong("manhuntBanStart", System.currentTimeMillis()), 1, TimeUnit.DAYS)) { this.manhuntQuits = 0; this.sendMessage(true, "%sFor not abandoning games in the past 24 hours, your quits have been reset.", GREEN); } } else { long banStart = this.extraData.getLong("manhuntBanStart", System.currentTimeMillis()); long duration = this.getManhuntBanDuration(); if (Miscellaneous.enoughTimeHasPassed(banStart, duration)) { this.sendMessage(true, "%sYou have been unbanned from the Manhunt public queue.", GREEN); this.unBanFromManhunt(); this.extraData.setBoolean("manhuntBanned", this.isBannedFromManhunt); } } if (increase) { this.extraData.setNumber("manhuntQuits", this.manhuntQuits); this.updateProfile(ProfileColumn.EXTRA); } } public int getManhuntBanHours() { switch (this.manhuntQuits) { case 0: case 1: return 0; case 2: return 3; case 3: return 8; default: return 24; } } public long getManhuntBanDuration() { int hours = this.getManhuntBanHours(); return hours * 60 * 60 * 1000L; } public void unBanFromManhunt() { this.isBannedFromManhunt = false; } public static boolean isNickname(String name) { return getFromNickname(name) != null; } public static SmartPlayer getFromNickname(String name) { for (SmartPlayer smartPlayer : getSmartPlayers()) { if (smartPlayer.isAndHasNick() && smartPlayer.nick.getName().equalsIgnoreCase(name)) { return smartPlayer; } } return null; } public static String getActualNickname(String name) { SmartPlayer smartPlayer = getFromNickname(name); if (smartPlayer != null) { return smartPlayer.nick.getName(); } return null; } public List getMessages(ChatType chatType, int page) { List queried = getMessagesOfType(chatType); List results = new ArrayList<>(); int startingIndex = PUNISHMENTS_PAGE_LENGTH * (page - 1); for (int i = 0; i < PUNISHMENTS_PAGE_LENGTH; i++) { int index = startingIndex + i; if (index >= queried.size()) { break; } results.add(queried.get(index)); } return results; } public List getMessagesOfType(ChatType chatType) { List queried = chatType == null ? Miscellaneous.getList(this.messages) : Miscellaneous.filter(this.messages, chatMessage -> chatMessage.getChatType() == chatType); return Miscellaneous.reverse(queried); } public BaseComponent getMessagesComponent(ChatType chatType, int page) { ComponentBuilder componentBuilder = new ComponentBuilder(); String baseCommand = "/messages " + this.getName() + " "; if (chatType != null) { baseCommand += toComparable(chatType); } if (page > 1) { componentBuilder.command(GOLD + "<<", baseCommand + (page - 1), AQUA + "Click here to view the previous page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.append(" "); componentBuilder.gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.PURPLE, ComponentBuilder.PINK).append(" %s[%d] ", GOLD, page).gradientLine(PlayerUtilities.HALF_LINE_LENGTH, ComponentBuilder.PINK, ComponentBuilder.PURPLE).append(" "); if (page < this.getMaxMessagePage(chatType)) { componentBuilder.command(GOLD + ">>", baseCommand + (page + 1), AQUA + "Click here to view the next page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.newLine(); for (ChatMessage message : this.getMessages(chatType, page)) { componentBuilder.append(message.getComponent(chatType == null)).newLine(); } componentBuilder.gradientLine(ComponentBuilder.PURPLE, ComponentBuilder.PINK, ComponentBuilder.PURPLE); return componentBuilder.getResult(); } public int getMaxMessagePage(ChatType chatType) { return (int) Math.ceil(this.getMessagesOfType(chatType).size() / (double) PUNISHMENTS_PAGE_LENGTH); } public boolean hasActivePunishment(PunishmentType punishmentType) { return this.getLatestActivePunishment(punishmentType) != null; } public boolean isBanned() { if (this.isAdministrator()) { return false; } return this.hasActivePunishment(PunishmentType.BAN) || this.hasActivePunishment(PunishmentType.IP_BAN); } public boolean isMuted() { if (this.isAdministrator()) { return false; } return this.hasActivePunishment(PunishmentType.MUTE); } public void mute(SmartPlayer punisher, long duration, String reason) { this.createPunishment(punisher, PunishmentType.MUTE, duration, reason); this.sendMessage(true, this.getLatestMuteReason()); } public void unMute(SmartPlayer revoker, String reason) { this.revokeLatestPunishment(PunishmentType.MUTE, revoker, reason); this.sendSuccessMessage("You are no longer muted."); } public void ban(SmartPlayer punisher, long duration, String reason) { this.createPunishment(punisher, PunishmentType.BAN, duration, reason); if (this.isOnline()) { this.proxiedPlayer.disconnect(this.getLatestBanReason()); } } public void ipBan(SmartPlayer smartPlayer, long duration, String reason) { this.createPunishment(smartPlayer, PunishmentType.IP_BAN, duration, reason); if (this.isOnline()) { this.proxiedPlayer.disconnect(this.getLatestBanReason()); } } public void unBan(SmartPlayer revoker, String reason) { if (this.hasActivePunishment(PunishmentType.BAN)) { this.revokeLatestPunishment(PunishmentType.BAN, revoker, reason); } else { this.revokeLatestPunishment(PunishmentType.IP_BAN, revoker, reason); } this.queueSuccessMessageForJoin("You are no longer banned."); } public void warn(SmartPlayer punisher, long duration, String reason) { this.createPunishment(punisher, PunishmentType.WARNING, duration, reason); this.sendMessage(true, this.getLatestWarnReason()); } private static final int PUNISHMENTS_PAGE_LENGTH = 5; public List getPunishments(PunishmentType punishmentType, boolean activeOnly, int page) { List queried = getPunishmentsOfType(punishmentType, activeOnly); List results = new ArrayList<>(); int startingIndex = PUNISHMENTS_PAGE_LENGTH * (page - 1); for (int i = 0; i < PUNISHMENTS_PAGE_LENGTH; i++) { int index = startingIndex + i; if (index >= queried.size()) { break; } results.add(queried.get(index)); } return results; } public List getPunishmentsOfType(PunishmentType punishmentType, boolean activeOnly) { List queried = punishmentType == null ? Miscellaneous.filter(this.punishments, punishment -> !activeOnly || punishment.isActive()) : Miscellaneous.filter(this.punishments, punishment -> punishment.getType() == punishmentType && (!activeOnly || punishment.isActive())); return Miscellaneous.reverse(queried); } public BaseComponent getPunishmentsComponent(PunishmentType punishmentType, boolean activeOnly, int page) { ComponentBuilder componentBuilder = new ComponentBuilder(); String baseCommand = "/punishments " + (activeOnly ? "view" : "viewactive") + " " + this.getName() + " "; if (punishmentType != null) { baseCommand += toComparable(punishmentType) + " "; } if (page > 1) { componentBuilder.command(GOLD + "<<", baseCommand + (page - 1), AQUA + "Click here to view the previous page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.append(" "); componentBuilder.gradientLine(30, ComponentBuilder.PINK_RED, ComponentBuilder.RED_ORANGE).append(" %s[%d] ", GOLD, page).gradientLine(30, ComponentBuilder.RED_ORANGE, ComponentBuilder.PINK_RED).append(" "); if (page < this.getMaxPunishmentPage(punishmentType, activeOnly)) { componentBuilder.command(GOLD + ">>", baseCommand + (page + 1), AQUA + "Click here to view the next page."); } else { componentBuilder.append(GOLD + "||"); } componentBuilder.newLine(); for (Punishment punishment : this.getPunishments(punishmentType, activeOnly, page)) { componentBuilder.append(getPunishmentComponent(punishment)).newLine(); } componentBuilder.gradientLine(ComponentBuilder.PINK_RED, ComponentBuilder.RED_ORANGE, ComponentBuilder.PINK_RED); return componentBuilder.getResult(); } public static BaseComponent getPunishmentComponent(Punishment punishment) { ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.append(GRAY).append("[").append(WHITE).append(punishment.getID()).append(GRAY).append("] ["); componentBuilder.color(ComponentBuilder.RED).append(Text.prettify(punishment.getType()).toUpperCase()).append(GRAY).append("] ["); componentBuilder.color(ComponentBuilder.AQUA).append(Text.getDateTimeString(punishment.getTime())).append(GRAY).append("]"); componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.RED_ORANGE).append(" - ID: ").color(Color.PINK).append(punishment.getBanID()).newLine(); SmartPlayer punisher = SmartPlayer.getSmartPlayer(punishment.getPunisher()); componentBuilder.color(ComponentBuilder.RED_ORANGE).append(" - Punisher: ").color(ComponentBuilder.GREEN).append(punishment.wasPunishedByConsole() ? "Console" : (punisher == null ? punishment.getPunisher() : punisher.getFormattedName())).newLine().color(ComponentBuilder.RED_ORANGE).append(" - Duration: ").color(Color.PINK).append(punishment.isIndefinite() ? "Permanent" : getShortTextualFormattedTime(punishment.getDuration())).newLine(); if (!punishment.isIndefinite()) { componentBuilder.color(ComponentBuilder.RED_ORANGE).append(" - Expires: ").color(ComponentBuilder.YELLOW).append(Text.getDateTimeString(punishment.getTime() + punishment.getDuration())).newLine(); } componentBuilder.color(ComponentBuilder.RED_ORANGE).append(" - Reason: ").color(ComponentBuilder.WHITE).append(punishment.hasReason() ? punishment.getReason() : "None"); if (!punishment.isActive()) { componentBuilder.newLine(); SmartPlayer revoker = SmartPlayer.getSmartPlayer(punishment.getRevoker()); componentBuilder.color(ComponentBuilder.YELLOW_GREEN).append(" - Revoker: ").color(ComponentBuilder.YELLOW).append(punishment.wasRevokedByConsole() ? "Console" : (revoker == null ? punishment.getRevoker() : revoker.getFormattedName())).newLine(); componentBuilder.color(ComponentBuilder.YELLOW_GREEN).append(" - Revoked: ").color(Color.PINK).append(Text.getDateString(punishment.getRevokeTime())).append(" ").append(Text.getTimeString(punishment.getRevokeTime())); } return componentBuilder.getResult(); } public int getMaxPunishmentPage(PunishmentType punishmentType, boolean activeOnly) { return (int) Math.ceil(this.getPunishmentsOfType(punishmentType, activeOnly).size() / (double) PUNISHMENTS_PAGE_LENGTH); } public void kick() { this.kick("Goodbye."); } public void kick(String format, Object... data) { if (this.isOnline()) { this.proxiedPlayer.disconnect(new TextComponent(RelayUtils.format(format, data))); } } public void kick(BaseComponent baseComponent) { if (this.isOnline()) { this.proxiedPlayer.disconnect(baseComponent); } } public void kick(ComponentBuilder componentBuilder) { this.kick(componentBuilder.getResult()); } public void createPunishment(SmartPlayer smartPlayer, PunishmentType punishmentType, long duration, String reason) { Punishment punishment = new Punishment(this.punishments.size(), smartPlayer == null ? null : smartPlayer.getUUID(), this.getUUID(), punishmentType, duration, reason, (this.proxiedPlayer != null && punishmentType == PunishmentType.IP_BAN) ? Utilities.parseAddress(this.proxiedPlayer.getSocketAddress()) : ""); this.punishments.add(punishment); BotUtils.punish(punishment); this.onPunishmentsChange(); } public BaseComponent getBanMessage() { Punishment punishment = this.getLatestActivePunishment(PunishmentType.BAN); if (punishment == null) { punishment = this.getLatestActivePunishment(PunishmentType.IP_BAN); } ComponentBuilder componentBuilder = new ComponentBuilder(); long timeRemaining = punishment.getDuration() - (System.currentTimeMillis() - punishment.getTime()); if (punishment.isIndefinite()) { componentBuilder.color(YELLOW).append("Duration: "); componentBuilder.color(ComponentBuilder.PINK).append("Permanent"); } else { componentBuilder.color(YELLOW).append("Time Remaining: "); long daysRemaining = timeRemaining / (24 * 60 * 60 * 1000L); String timeRemainingString = daysRemaining > 0 ? (daysRemaining + " days") : Text.getTextualFormattedTime(timeRemaining, false); componentBuilder.color(ComponentBuilder.PINK).append(timeRemainingString); } componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.PINK_RED).append("Reason: ").color(WHITE).append(punishment.hasReason() ? punishment.getReason() : "None"); componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.RED_ORANGE).append("%s ID: ", Text.prettify(punishment.getType())).color(Color.PINK).append("#").append(punishment.getBanID()); componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.AQUA).append("Appeal: ").link(DARK_PURPLE + Main.DISCORD_URL, Main.DISCORD_URL, false); return componentBuilder.getResult(); } public String getExpiryDate(Punishment punishment) { return Text.getDateTimeString(punishment.getTime() + punishment.getDuration()); } public BaseComponent getLatestBanReason() { Punishment ban = this.getLatestActivePunishment(PunishmentType.BAN); if (ban == null) { ban = this.getLatestActivePunishment(PunishmentType.IP_BAN); } ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.color(ComponentBuilder.BLUE).append(BOLD).append("Relay").newLine().newLine(); componentBuilder.color(RED); componentBuilder.append("You have been banned.").newLine(); componentBuilder.appendComponent(this.getPunishmentComponent(ban, true, true, true, true)); return componentBuilder.getResult(); } public BaseComponent getPunishmentComponent(Punishment punishment, boolean showDuration, boolean showID, boolean showAppeal, boolean showLink) { ComponentBuilder componentBuilder = new ComponentBuilder(); if (showDuration) { componentBuilder.color(YELLOW).append("Duration: "); componentBuilder.color(ComponentBuilder.PINK).append(punishment.isIndefinite() ? "Permanent" : getShortTextualFormattedTime(punishment.getDuration())).newLine(); } componentBuilder.color(ComponentBuilder.PINK_RED).append("Reason: ").color(WHITE).append(punishment.hasReason() ? punishment.getReason() : "None"); if (showID) { componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.RED_ORANGE).append("%s ID: ", Text.prettify(punishment.getType())).color(Color.PINK).append("#").append(punishment.getBanID()); } if (showAppeal) { componentBuilder.newLine(); componentBuilder.color(ComponentBuilder.AQUA).append("Appeal: ").link(showLink ? DARK_PURPLE + Main.DISCORD_URL : DARK_PURPLE + UNDERLINE.toString() + "Discord", Main.DISCORD_URL, false); } return componentBuilder.getResult(); } public BaseComponent getLatestMuteReason() { Punishment mute = this.getLatestActivePunishment(PunishmentType.MUTE); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.color(Color.RED).line().newLine(); componentBuilder.color(ComponentBuilder.RED); componentBuilder.append("You have been muted.").newLine(); componentBuilder.append((this.getPunishmentComponent(mute, true, true, true, false))).newLine(); componentBuilder.color(Color.RED).line(); return componentBuilder.getResult(); } public BaseComponent getLatestWarnReason() { Punishment warn = this.getLatestActivePunishment(PunishmentType.WARNING); ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.ORANGE, ComponentBuilder.YELLOW, ComponentBuilder.ORANGE).newLine(); componentBuilder.color(ComponentBuilder.RED); componentBuilder.append("You have been warned.").newLine(); componentBuilder.append(this.getPunishmentComponent(warn, false, false, false, false)).newLine(); componentBuilder.gradientLine(ComponentBuilder.ORANGE, ComponentBuilder.YELLOW, ComponentBuilder.ORANGE); return componentBuilder.getResult(); } public Punishment getLatestActivePunishment(PunishmentType punishmentType) { for (Punishment punishment : Miscellaneous.reverse(this.punishments)) { if (punishment.getType() == punishmentType && punishment.isActive() && !punishment.hasExpired()) { return punishment; } } return null; } public void revokeLatestPunishment(PunishmentType punishmentType, SmartPlayer revoker, String reason) { if (this.hasActivePunishment(punishmentType)) { this.revokePunishment(this.getLatestActivePunishment(punishmentType), revoker, reason); } } public List getPunishments(PunishmentType punishmentType, boolean activeOnly) { List punishments = new ArrayList<>(); for (Punishment punishment : this.punishments) { if ((punishmentType == null || punishment.getType() == punishmentType) && (!activeOnly || punishment.isActive())) { punishments.add(punishment); } } return punishments; } public void clearPunishments() { if (!this.punishments.isEmpty()) { this.punishments.clear(); this.onPunishmentsChange(); } } public void onPunishmentsChange() { this.updateProfile(ProfileColumn.PUNISHMENTS); } public void checkForUpdates() { boolean updated = false; for (Punishment punishment : this.punishments) { if (punishment.checkIfExpire()) { punishment.revokeForExpiry(); if (punishment.getType() == PunishmentType.MUTE) { this.sendSuccessMessage("Your mute has expired."); } updated = true; } } if (updated) { this.onPunishmentsChange(); } this.checkIfManhuntBanned(false); if (this.isOnline()) { if (this.discordEarned > 0) { this.sendDiscordRewardsMessage(); } } } public void sendDiscordRewardsMessage() { long tokens = RelayUtils.getTokens(this.discordEarned * DISCORD_TOKENS, this.getHighestGroup(), this.getChampionTier()); long xp = RelayUtils.getXP(this.discordEarned * DISCORD_EXP, this.getHighestGroup(), this.getChampionTier()); this.sendSuccessMessage("You have received %s%d%s tokens and %s%d%s network EXP for your Discord activity.", GOLD, tokens, GREEN, AQUA, xp, GREEN); this.discordEarned = 0; this.extraData.setInt("discordEarned", this.discordEarned); this.updateProfile(ProfileColumn.EXTRA); } public boolean hasPunishment(int id) { return this.getPunishment(id) != null; } public Punishment getPunishment(int id) { for (Punishment punishment : this.punishments) { if (punishment.getID() == id) { return punishment; } } return null; } public void revokePunishment(Punishment punishment, SmartPlayer punisher, String reason) { punishment.revoke(punisher == null ? null : punisher.getUUID(), reason); this.onPunishmentsChange(); } public List getPunishmentIds() { return Miscellaneous.processList(this.punishments, Punishment::getID); } public String getNickname() { return this.nick == null ? null : this.nick.getName(); } public void sendPluginMessage(Message message) { MessageUtils.sendByPlayer(this.proxiedPlayer, message); } public void sendPluginMessage(Command command, Object... arguments) { MessageUtils.sendByPlayer(this.proxiedPlayer, command, arguments); } public void playSound(Sound sound) { this.sendPluginMessage(Command.SOUND, this.getUUID(), sound); } public void onGameEnd() { this.updateProfile(ProfileColumn.TIME_PLAYED); if (this.lastGameType == GameType.DUELS_CUSTOM) { Main.broadcastPublicQueues(); } } public void levelUp(MainGame mainGame) { this.lastLevelUp = System.currentTimeMillis(); this.broadcastUpdateMessage(); int newLevel = this.calculateLevel(mainGame); ComponentBuilder message = new ComponentBuilder(); if (mainGame == null) { message.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA, ComponentBuilder.BLUE).newLine(); message.color(GOLD, BOLD).append("LEVEL UP!").newLine(); message.color(ComponentBuilder.GREEN).append("You are now Network %s%sLEVEL %d!", AQUA, BOLD, newLevel); List unlockedCosmetics = Cosmetic.getCosmeticRewardsForLevel(newLevel); for (Cosmetic unlockedCosmetic : unlockedCosmetics) { message.newLine(); message.color(ComponentBuilder.GREEN).append(" + ").append(unlockedCosmetic.getPrettyName()); Miscellaneous.addIfAbsent(this.cosmetics, unlockedCosmetic); } for (GameCosmetic gameCosmetic : GameCosmetic.getGameCosmeticRewardsForLevel(newLevel)) { message.newLine(); message.color(ComponentBuilder.GREEN).append(" + ").append(gameCosmetic.getPrettyName()); for (MainGame game : gameCosmetic.getMainGames()) { Miscellaneous.addIfAbsent(this.getGameCosmeticList(game), gameCosmetic); } } message.newLine(); message.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA, ComponentBuilder.BLUE); this.sendMessage(message); } else { /* message.color(ComponentBuilder.YELLOW).line().newLine(); message.append("You are now %s level %s%s%d!", mainGame.getName(), AQUA, BOLD, this.calculateLevel(mainGame)).newLine(); message.color(ComponentBuilder.YELLOW).line(); */ } } public List getGameCosmeticList(MainGame mainGame) { return this.gameCosmetics.get(mainGame); } public List getActiveGameCosmeticList(MainGame mainGame) { return this.activeGameCosmetics.get(mainGame); } public int calculateLevel(MainGame mainGame) { return RelayUtils.calculateLevel(mainGame, this.getXp(mainGame)); } public long getXp(MainGame mainGame) { if (mainGame == null) { return this.xp; } return this.gameExperience.getOrDefault(mainGame, 0L); } public long getElo(MainGame mainGame) { return this.gameElo.getOrDefault(mainGame, 0L); } public int calculateLevel() { return this.calculateLevel(null); } public League getLeague(MainGame mainGame) { return League.getLeague(this.getElo(mainGame)); } public void sendToHub() { if (this.getServer() != null) { if (this.lastGameType != null && !this.getServer().isHub() && this.lastGameType.getMainGame() == MainGame.PARTY) { this.sendToServer(Server.getServerOfType(null, false)); } else { this.sendToServer(this.getServer().getHub()); } } else { this.sendToServer((MainGame) null); } } public long getLastDisconnectTime() { return this.lastDisconnectTime; } public void setLastDisconnectTime(long lastDisconnectTime) { this.lastDisconnectTime = lastDisconnectTime; } public List getQueuedMessages() { return this.queuedMessages; } public String getLastName() { return this.lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public long getFirstJoinTime() { return this.firstJoinTime; } public void setFirstJoinTime(long firstJoinTime) { this.firstJoinTime = firstJoinTime; } public long getPreviousLastJoinTime() { return this.previousLastJoinTime; } public void setPreviousLastJoinTime(long previousLastJoinTime) { this.previousLastJoinTime = previousLastJoinTime; } public long getJoinTime() { return this.joinTime; } public void setJoinTime(long joinTime) { this.joinTime = joinTime; } public boolean isNewLogin() { return this.isNewLogin; } public void setNewLogin(boolean newLogin) { isNewLogin = newLogin; } public List getFriends() { return this.friends; } public void setFriends(List friends) { this.friends = friends; } public List getBlockedPlayers() { return this.blockedPlayers; } public void setBlockedPlayers(List blockedPlayers) { this.blockedPlayers = blockedPlayers; } public List getPreviousUsernames() { return this.previousUsernames; } public void setPreviousUsernames(List previousUsernames) { this.previousUsernames = previousUsernames; } public synchronized List getGroups() { return this.groups; } public void setGroups(List groups) { this.groups = groups; } public long getCredits() { return this.credits; } public void setCredits(long credits) { this.credits = credits; } public long getXp() { return this.xp; } public void setXp(long xp) { this.xp = xp; } public List getCosmetics() { return this.cosmetics; } public void setCosmetics(List cosmetics) { this.cosmetics = cosmetics; } public List getActiveCosmetics() { return this.activeCosmetics; } public void setActiveCosmetics(List activeCosmetics) { this.activeCosmetics = activeCosmetics; } public boolean isNicked() { return this.isNicked; } public void setNicked(boolean nicked) { isNicked = nicked; } public Nick getNick() { return this.nick; } public ChatFilter getChatFilter() { return this.chatFilter; } public void setChatFilter(ChatFilter chatFilter) { this.chatFilter = chatFilter; } public MessageFilter getMessageFilter() { return this.messageFilter; } public void setMessageFilter(MessageFilter messageFilter) { this.messageFilter = messageFilter; } public FriendFilter getFriendFilter() { return this.friendFilter; } public void setFriendFilter(FriendFilter friendFilter) { this.friendFilter = friendFilter; } public PartyFilter getPartyFilter() { return this.partyFilter; } public void setPartyFilter(PartyFilter partyFilter) { this.partyFilter = partyFilter; } public DuelFilter getDuelFilter() { return this.duelFilter; } public void setDuelFilter(DuelFilter duelFilter) { this.duelFilter = duelFilter; } public List getLatestMessagesWithin3Seconds() { return this.latestMessagesWithin3Seconds; } public void setLatestMessagesWithin3Seconds(List latestMessagesWithin3Seconds) { this.latestMessagesWithin3Seconds = latestMessagesWithin3Seconds; } public int getCommandsInPast5Seconds() { return this.commandsInPast5Seconds; } public void setCommandsInPast5Seconds(int commandsInPast5Seconds) { this.commandsInPast5Seconds = commandsInPast5Seconds; } public GameType getCurrentQueue() { return this.currentQueue; } public void setCurrentQueue(GameType currentQueue) { this.currentQueue = currentQueue; } public boolean isHasUpdatedQueueScoreboard() { return this.hasUpdatedQueueScoreboard; } public void setHasUpdatedQueueScoreboard(boolean hasUpdatedQueueScoreboard) { this.hasUpdatedQueueScoreboard = hasUpdatedQueueScoreboard; } public ChatType getChatType() { return this.chatType; } public void setCurrentParty(Party currentParty) { this.currentParty = currentParty; this.sendPartyData(); } public List getInvitedParties() { return this.invitedParties; } public void setInvitedParties(List invitedParties) { this.invitedParties = invitedParties; } public Map getInvitedDuels() { return this.invitedDuels; } public void setInvitedDuels(Map invitedDuels) { this.invitedDuels = invitedDuels; } public List getReceivedFriendRequests() { return this.receivedFriendRequests; } public void setReceivedFriendRequests(List receivedFriendRequests) { this.receivedFriendRequests = receivedFriendRequests; } public Server getLastServer() { return this.lastServer; } public void setLastServer(Server lastServer) { this.lastServer = lastServer; } public int getManhuntQuits() { return this.manhuntQuits; } public void setManhuntQuits(int manhuntQuits) { this.manhuntQuits = manhuntQuits; this.checkIfManhuntBanned(true); } public boolean isBannedFromManhunt() { return this.isBannedFromManhunt; } public void setBannedFromManhunt(boolean bannedFromManhunt) { this.isBannedFromManhunt = bannedFromManhunt; } public void report(SmartPlayer toReport, ReportType reportType, String reason) { if (reason != null) { if (!SwearUtils.canSendMessageAtLevel(reason, ChatFilter.HIGH)) { this.sendErrorMessage("Your reason must not contain vulgar language."); return; } } else if (reportType == ReportType.BUG) { this.sendErrorMessage("You must explain a bug report!"); return; } Report report = new Report(this, toReport, reportType, this.getServer(), reason); this.lastReportTime = System.currentTimeMillis(); if (reportType == ReportType.BUG) { this.sendSuccessMessage("Submitted bug report."); return; } this.sendSuccessMessage("Submitted report on %s%s for %s%s.", toReport.getFormattedNameOrNick(), GREEN, reportType.toString(), reason != null ? " (" + reason + ")" : ""); } public boolean canReport() { if (this.isAdministrator()) { return true; } return !this.isMuted() && Miscellaneous.enoughTimeHasPassed(this.lastReportTime, 5, TimeUnit.MINUTES); } public String getFormattedNameOrNick() { return this.isAndHasNick() ? this.getFormattedNickname() : this.getFormattedName(); } public String getNameOrNick() { return this.isAndHasNick() ? this.getNickname() : this.getName(); } public boolean isReceiveReportMessages() { return this.extraData.getBoolean("receiveReportMessages", true); } public void toggleReceiveReportMessages() { this.receiveReportMessages = !this.receiveReportMessages; this.extraData.setBoolean("receiveReportMessages", this.receiveReportMessages); this.updateProfile(ProfileColumn.EXTRA); } public long getLastReportTime() { return this.lastReportTime; } public void onReportDismiss(Report report) { ComponentBuilder componentBuilder = new ComponentBuilder(); if (report.isAccepted()) { componentBuilder.color(ComponentBuilder.GREEN); } else { componentBuilder.color(ComponentBuilder.YELLOW); } componentBuilder.line().newLine(); componentBuilder.color(ComponentBuilder.YELLOW); componentBuilder.append("After review by staff, your report on %s ", report.getReported().getFormattedNameOrNick()).color(ComponentBuilder.YELLOW).append("for ").color(report.getReportType().getColor()).append(report.getReportType().toString()).color(ComponentBuilder.YELLOW).append(" has been "); if (report.isAccepted()) { componentBuilder.color(ComponentBuilder.GREEN).append("accepted"); } else { componentBuilder.color(ComponentBuilder.RED).append("denied"); } componentBuilder.color(ComponentBuilder.YELLOW).append(".").newLine(); if (report.hasDismissMessage()) { componentBuilder.color(Color.PINK).append("Message: ").color(ComponentBuilder.WHITE).append(report.getDismissMessage()).newLine(); } if (report.getTokensRewarded() != 0) { componentBuilder.append(GOLD).append("Tokens: %d", report.getTokensRewarded()).newLine(); this.credits += RelayUtils.getTokens(report.getTokensRewarded(), this.getHighestGroup(), this.getChampionTier()); this.updateProfile(ProfileColumn.CREDITS); } componentBuilder.color(ComponentBuilder.YELLOW).line(); this.sendMessage(true, componentBuilder.getResult()); } public void sendChampionPurchaseMessage() { ComponentBuilder componentBuilder = new ComponentBuilder(); componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE).newLine(); if (this.isInGroup(Group.CHAMPION)) { componentBuilder.color(java.awt.Color.YELLOW).append("In order to use this feature, you must upgrade to a higher Premium tier.").newLine(); componentBuilder.color(GOLD).append("Upgrade: ").link("https://store.relaymc.net/package/5897406").newLine(); } else { componentBuilder.color(java.awt.Color.YELLOW).append("In order to use this feature, you must have Premium rank.").newLine(); componentBuilder.color(GOLD).append("Buy Premium: ").link(RelayUtils.CHAMPION_PURCHASE_LINK).newLine(); } componentBuilder.gradientLine(ComponentBuilder.BLUE, ComponentBuilder.AQUA_BLUE, ComponentBuilder.BLUE); this.sendMessage(componentBuilder); } public void giveMonthlyChampionTokens() { int tokens = new int[]{0, 5000, 7500, 9000, 10000, 11000, 12000, 12500, 13000, 13500, 13750, 14000, 14250, 14500, 14750, 15000, 15250, 15500, 15750, 15600, 16250, 16500, 16750, 17000, 17250}[Math.min(this.getChampionTier(), 24)]; this.sendSuccessMessage("You received %s%d%s tokens!", GOLD, tokens, GREEN); this.credits += tokens; this.updateProfile(ProfileColumn.CREDITS); } public boolean equals(SmartPlayer smartPlayer) { return smartPlayer != null && smartPlayer.uuid.equals(this.uuid); } public void reQueue() { if (this.lastGameType != null) { this.queueGame(this.lastGameType); } else { this.sendErrorMessage("There game for you to requeue!"); } } public void duelsRematchOption(SmartPlayer opponent, GameType gameType, String queueData) { this.rematchOpponent = opponent; this.rematchGameType = gameType; this.rematchQueueData = QueueData.fromString(queueData); this.acceptRematch = false; ComponentBuilder message = new ComponentBuilder(Color.BLUE).line().newLine(); message.color(ComponentBuilder.AQUA).append("Rematch ").append(opponent.getFormattedNameOrNick()).color(ComponentBuilder.AQUA).append("?").newLine(); message.command(GOLD + "Click here.", "/rematch " + opponent.getNameOrNick()).newLine(); message.color(Color.BLUE).line(); this.sendMessage(message); } public void rematch(SmartPlayer opponent) { if (opponent != null && opponent.rematchOpponent == this) { if (this.acceptRematch) { this.sendErrorMessage("You have already invited this player to a rematch!"); return; } this.acceptRematch = true; if (opponent.acceptRematch) { Queue.rematch(this, opponent, rematchGameType, rematchQueueData); } else { this.sendSuccessMessage("Rematch request sent."); Main.schedule(() -> { if (!opponent.acceptRematch) { ComponentBuilder message = new ComponentBuilder(GOLD.toString()).line().newLine(); message.append(this.getFormattedNameOrNick()).color(ComponentBuilder.AQUA).append(" would like to rematch!").newLine(); message.command(GOLD + "Click here to accept.", "/rematch " + this.getNameOrNick()).newLine(); message.color(GOLD).line(); opponent.sendMessage(message); } }, 2); } } else { this.sendErrorMessage("Your rematch opportunity has expired."); } } public void sendToLimbo() { this.sendToServer("limbo"); } public void sendLimboQueuePosition(boolean info) { this.sendPluginMessage(Command.OTHER, "limboqueueposition", this.getUUID(), LIMBO_QUEUE.indexOf(this) + 1, LIMBO_QUEUE.size()); if (info) { ComponentBuilder componentBuilder = new ComponentBuilder().gradientLine(Color.GREEN, ComponentBuilder.YELLOW_GREEN, Color.GREEN).newLine(); componentBuilder.color(Color.RED).append("The network is currently at capacity. You must wait until a player leaves to log on. Premium rank users bypass this restriction.").newLine(); //componentBuilder.bold().color(GOLD).append("YOUR POSITION: ").reset().color(Color.WHITE).append(LIMBO_QUEUE.indexOf(this) + 1).color(ComponentBuilder.AQUA).append("/%d", LIMBO_QUEUE.size()).newLine(); componentBuilder.gradientLine(ComponentBuilder.GREEN, ComponentBuilder.YELLOW_GREEN, Color.GREEN); this.sendMessage(componentBuilder); } } public void onMoveAFK() { if (!this.isInGroupOrHigher(Group.CHAMPION)) { if (Main.networkIsFull()) { this.enterLimboQueue(); return; } } if (this.lastHubServer != null) { this.sendToServer(this.lastHubServer.getMainGame()); } else { this.sendToHub(); } } public void onAFKAgain() { this.sendErrorMessage("You have gone AFK for too long. Your position in the queue has been reset."); LIMBO_QUEUE.remove(this); } public boolean isInLimboQueue() { return LIMBO_QUEUE.contains(this); } public void skipLimboQueue() { LIMBO_QUEUE.remove(this); this.sendToHub(); } public void chat(String message) { this.sendPluginMessage(Command.OTHER, "chat", this.getUUID(), message); } public void playSuccessSound() { this.playSound(Sound.BLOCK_NOTE_BLOCK_BELL); } public boolean hasDiscordLinked() { return this.discordId != 0; } public void setDiscordId(long id) { this.discordId = id; this.updateProfile(ProfileColumn.DISCORD_ID); } public void unlinkDiscord() { BotUtils.ID_TO_UUID.remove(this.discordId); this.discordId = 0; this.updateProfile(ProfileColumn.DISCORD_ID); } public Long getDiscordId() { return this.discordId; } public void onDiscordMessage(String message) { int messagesSent = this.extraData.getInt("discordRewardMessagesSent", 0); if (Miscellaneous.enoughSecondsHavePassed(this.lastDiscordMessage, RelayUtils.testing ? 1 : 10)) { this.lastDiscordMessage = System.currentTimeMillis(); messagesSent++; if (messagesSent % (RelayUtils.testing ? 10 : 25) == 0) { this.discordMessagesReward(); } this.extraData.setInt("discordRewardMessagesSent", messagesSent); this.updateProfile(ProfileColumn.EXTRA); } } private static final long DISCORD_TOKENS = RelayUtils.testing ? 125 : 5; private static final long DISCORD_EXP = RelayUtils.testing ? 5000 : 15; public void discordMessagesReward() { this.credits += DISCORD_TOKENS; this.xp += DISCORD_EXP; this.discordEarned++; this.extraData.setInt("discordEarned", this.discordEarned); this.updateProfile(ProfileColumn.EXTRA); this.updateProfile(ProfileColumn.CREDITS); this.updateProfile(ProfileColumn.EXPERIENCE); } public void onPurchaseChampion() { this.extraData.setInt("championTier", this.getChampionTier() + 1); this.updateProfile(ProfileColumn.EXTRA); this.giveMonthlyChampionTokens(); } public int getChampionTier() { int i = this.extraData.has("championTier") ? this.extraData.getInt("championTier", 0) : 0; if (i == 0 && this.hasChampion()) { return 1000; } return i; } public void onPurchaseSkipTier() { this.extraData.setInt("championTier", this.getChampionTier() + 1); if (!this.isInGroup(Group.CHAMPION)) { this.addToGroup(Group.CHAMPION); this.updateProfile(ProfileColumn.GROUPS); } this.updateProfile(ProfileColumn.EXTRA); this.sendMessage(true, "%sYou have skipped a tier and are now %s.", GREEN, this.getFormattedName()); } }