From 6b0f2355b6126a59303b2008f3a5a583149bbc17 Mon Sep 17 00:00:00 2001 From: aBo0oDyy <35378106+aBo0oDyy@users.noreply.github.com> Date: Fri, 12 Apr 2019 15:25:54 +0300 Subject: [PATCH] configurable tps color --- .../expansion/server/ServerExpansion.java | 579 +++++++++--------- target/PAPI-Expansion-Server.jar | Bin 7693 -> 7855 bytes 2 files changed, 296 insertions(+), 283 deletions(-) diff --git a/src/main/java/com/extendedclip/papi/expansion/server/ServerExpansion.java b/src/main/java/com/extendedclip/papi/expansion/server/ServerExpansion.java index 84f83ef..3d0b1e1 100644 --- a/src/main/java/com/extendedclip/papi/expansion/server/ServerExpansion.java +++ b/src/main/java/com/extendedclip/papi/expansion/server/ServerExpansion.java @@ -1,283 +1,296 @@ -/* - * - * Server-Expansion - * Copyright (C) 2018 Ryan McCarthy - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - */ -package com.extendedclip.papi.expansion.server; - -import java.lang.reflect.Field; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import me.clip.placeholderapi.PlaceholderAPIPlugin; -import me.clip.placeholderapi.expansion.Cacheable; -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import me.clip.placeholderapi.util.TimeUtil; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - -public class ServerExpansion extends PlaceholderExpansion implements Cacheable { - - private final Map dateFormats = new HashMap(); - private final int MB = 1048576; - private final Runtime runtime = Runtime.getRuntime(); - private Object craftServer; - private Field tps; - private String version; - - private final String VERSION = getClass().getPackage().getImplementationVersion(); - - public ServerExpansion() { - try { - version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; - craftServer = Class.forName("net.minecraft.server." + version + ".MinecraftServer").getMethod("getServer").invoke(null); - tps = craftServer.getClass().getField("recentTps"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void clear() { - craftServer = null; - tps = null; - version = null; - dateFormats.clear(); - } - - @Override - public String getIdentifier() { - return "server"; - } - - @Override - public String getPlugin() { - return null; - } - - @Override - public String getAuthor() { - return "clip"; - } - - @Override - public String getVersion() { - return VERSION; - } - - @Override - public String onPlaceholderRequest(Player p, String identifier) { - - switch (identifier) { - case "tps": - return getTps(null); - case "online": - return String.valueOf(Bukkit.getOnlinePlayers().size()); - case "max_players": - return String.valueOf(Bukkit.getMaxPlayers()); - case "unique_joins": - return String.valueOf(Bukkit.getOfflinePlayers().length); - case "uptime": - return getPlaceholderAPI().getUptime(); - case "has_whitelist": - return Bukkit.getServer().hasWhitelist() ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse(); - } - - if (identifier.startsWith("tps_")) { - identifier = identifier.replace("tps_", ""); - return getTps(identifier); - } - - if (identifier.startsWith("online_")) { - - identifier = identifier.replace("online_", ""); - - int i = 0; - - for (Player o : Bukkit.getOnlinePlayers()) { - if (o.getWorld().getName().equals(identifier)) { - i = i + 1; - } - } - return String.valueOf(i); - } - - if (identifier.startsWith("countdown_")) { - String time = identifier.replace("countdown_", ""); - - if (time.indexOf("_") == -1) { - - Date then = null; - - try { - then = PlaceholderAPIPlugin.getDateFormat().parse(time); - } catch (Exception e) { - return null; - } - - Date now = new Date(); - - long between = then.getTime() - now.getTime(); - - if (between <= 0) { - return "0"; - } - - return TimeUtil.getTime((int) TimeUnit.MILLISECONDS.toSeconds(between)); - - } else { - - String[] parts = time.split("_"); - - if (parts.length != 2) { - return "invalid format and time"; - } - - time = parts[1]; - - String format = parts[0]; - - SimpleDateFormat f = null; - - try { - f = new SimpleDateFormat(format); - } catch (Exception e) { - return "invalid date format"; - } - - Date then = null; - - try { - then = f.parse(time); - } catch (Exception e) { - return "invalid date"; - } - - long t = System.currentTimeMillis(); - - long between = then.getTime() - t; - - if (between <= 0) { - return "0"; - } - - return TimeUtil.getTime((int) TimeUnit.MILLISECONDS.toSeconds(between)); - - } - } - - if (identifier.startsWith("time_")) { - - identifier = identifier.replace("time_", ""); - - if (dateFormats.containsKey(identifier)) { - return dateFormats.get(identifier).format(new Date()); - } - - try { - SimpleDateFormat format = new SimpleDateFormat(identifier); - - dateFormats.put(identifier, format); - - return format.format(new Date()); - } catch (NullPointerException | IllegalArgumentException ex) { - return null; - } - } - - if (identifier.startsWith("ram_")) { - - if (identifier.equals("ram_used")) { - return String.valueOf((runtime.totalMemory() - runtime.freeMemory()) / MB); - } - - if (identifier.equals("ram_free")) { - return String.valueOf(runtime.freeMemory() / MB); - } - - if (identifier.equals("ram_total")) { - return String.valueOf(runtime.totalMemory() / MB); - } - - if (identifier.equals("ram_max")) { - return String.valueOf(runtime.maxMemory() / MB); - } - } - - return null; - } - - private double[] tps() { - if (version == null || craftServer == null || tps == null) { - return new double[] { 0, 0, 0 }; - } - try { - return ((double[]) tps.get(craftServer)); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return new double[] { 0, 0, 0 }; - } - - private double fix(double tps) { - return Math.min(Math.round(tps * 100.0) / 100.0, 20.0); - } - - private String color(double tps) { - return ((tps > 18.0) ? ChatColor.GREEN : (tps > 16.0) ? ChatColor.YELLOW : ChatColor.RED).toString() - + ((tps > 20.0) ? "*" : "") + fix(tps); - } - - public String getTps(String arg) { - if (arg == null || arg.isEmpty()) { - StringBuilder sb = new StringBuilder(); - for (double t : tps()) { - sb.append(color(t)); - sb.append(", "); - } - return sb.toString(); - } - switch (arg) { - case "1": - case "one": - return String.valueOf(fix(tps()[0])); - case "5": - case "five": - return String.valueOf(fix(tps()[1])); - case "15": - case "fifteen": - return String.valueOf(tps()[2]); - case "1_colored": - case "one_colored": - return color(tps()[0]); - case "5_colored": - case "five_colored": - return color(tps()[1]); - case "15_colored": - case "fifteen_colored": - return color(tps()[2]); - } - return null; - } - -} +/* + * + * Server-Expansion + * Copyright (C) 2018 Ryan McCarthy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + */ +package com.extendedclip.papi.expansion.server; + +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import me.clip.placeholderapi.PlaceholderAPIPlugin; +import me.clip.placeholderapi.expansion.Cacheable; +import me.clip.placeholderapi.expansion.Configurable; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import me.clip.placeholderapi.util.TimeUtil; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +public class ServerExpansion extends PlaceholderExpansion implements Cacheable, Configurable { + + private final Map dateFormats = new HashMap(); + private final int MB = 1048576; + private final Runtime runtime = Runtime.getRuntime(); + private Object craftServer; + private Field tps; + private String version; + + private final String VERSION = getClass().getPackage().getImplementationVersion(); + + public ServerExpansion() { + try { + version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + craftServer = Class.forName("net.minecraft.server." + version + ".MinecraftServer").getMethod("getServer").invoke(null); + tps = craftServer.getClass().getField("recentTps"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void clear() { + craftServer = null; + tps = null; + version = null; + dateFormats.clear(); + } + + @Override + public String getIdentifier() { + return "server"; + } + + @Override + public String getPlugin() { + return null; + } + + @Override + public String getAuthor() { + return "clip"; + } + + @Override + public String getVersion() { + return VERSION; + } + + @Override + public Map getDefaults() { + Map defaults = new HashMap<>(); + defaults.put("tps_color.high", "&a"); + defaults.put("tps_color.medium", "&e"); + defaults.put("tps_color.low", "&c"); + return defaults; + } + + @Override + public String onPlaceholderRequest(Player p, String identifier) { + + switch (identifier) { + case "tps": + return getTps(null); + case "online": + return String.valueOf(Bukkit.getOnlinePlayers().size()); + case "max_players": + return String.valueOf(Bukkit.getMaxPlayers()); + case "unique_joins": + return String.valueOf(Bukkit.getOfflinePlayers().length); + case "uptime": + return getPlaceholderAPI().getUptime(); + case "has_whitelist": + return Bukkit.getServer().hasWhitelist() ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse(); + } + + if (identifier.startsWith("tps_")) { + identifier = identifier.replace("tps_", ""); + return getTps(identifier); + } + + if (identifier.startsWith("online_")) { + + identifier = identifier.replace("online_", ""); + + int i = 0; + + for (Player o : Bukkit.getOnlinePlayers()) { + if (o.getWorld().getName().equals(identifier)) { + i = i + 1; + } + } + return String.valueOf(i); + } + + if (identifier.startsWith("countdown_")) { + String time = identifier.replace("countdown_", ""); + + if (time.indexOf("_") == -1) { + + Date then = null; + + try { + then = PlaceholderAPIPlugin.getDateFormat().parse(time); + } catch (Exception e) { + return null; + } + + Date now = new Date(); + + long between = then.getTime() - now.getTime(); + + if (between <= 0) { + return "0"; + } + + return TimeUtil.getTime((int) TimeUnit.MILLISECONDS.toSeconds(between)); + + } else { + + String[] parts = time.split("_"); + + if (parts.length != 2) { + return "invalid format and time"; + } + + time = parts[1]; + + String format = parts[0]; + + SimpleDateFormat f = null; + + try { + f = new SimpleDateFormat(format); + } catch (Exception e) { + return "invalid date format"; + } + + Date then = null; + + try { + then = f.parse(time); + } catch (Exception e) { + return "invalid date"; + } + + long t = System.currentTimeMillis(); + + long between = then.getTime() - t; + + if (between <= 0) { + return "0"; + } + + return TimeUtil.getTime((int) TimeUnit.MILLISECONDS.toSeconds(between)); + + } + } + + if (identifier.startsWith("time_")) { + + identifier = identifier.replace("time_", ""); + + if (dateFormats.containsKey(identifier)) { + return dateFormats.get(identifier).format(new Date()); + } + + try { + SimpleDateFormat format = new SimpleDateFormat(identifier); + + dateFormats.put(identifier, format); + + return format.format(new Date()); + } catch (NullPointerException | IllegalArgumentException ex) { + return null; + } + } + + if (identifier.startsWith("ram_")) { + + if (identifier.equals("ram_used")) { + return String.valueOf((runtime.totalMemory() - runtime.freeMemory()) / MB); + } + + if (identifier.equals("ram_free")) { + return String.valueOf(runtime.freeMemory() / MB); + } + + if (identifier.equals("ram_total")) { + return String.valueOf(runtime.totalMemory() / MB); + } + + if (identifier.equals("ram_max")) { + return String.valueOf(runtime.maxMemory() / MB); + } + } + + return null; + } + + private double[] tps() { + if (version == null || craftServer == null || tps == null) { + return new double[] { 0, 0, 0 }; + } + try { + return ((double[]) tps.get(craftServer)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return new double[] { 0, 0, 0 }; + } + + private double fix(double tps) { + return Math.min(Math.round(tps * 100.0) / 100.0, 20.0); + } + + private String color(double tps) { + String low = this.getString("tps_color.low", "&c"); + String medium = this.getString("tps_color.medium", "&e"); + String high = this.getString("tps_color.high", "&a"); + return ChatColor.translateAlternateColorCodes('&', (tps > 18.0) ? high : (tps > 16.0) ? medium : low) + + ((tps > 20.0) ? "*" : "") + fix(tps); + } + + public String getTps(String arg) { + if (arg == null || arg.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (double t : tps()) { + sb.append(color(t)); + sb.append(", "); + } + return sb.toString(); + } + switch (arg) { + case "1": + case "one": + return String.valueOf(fix(tps()[0])); + case "5": + case "five": + return String.valueOf(fix(tps()[1])); + case "15": + case "fifteen": + return String.valueOf(tps()[2]); + case "1_colored": + case "one_colored": + return color(tps()[0]); + case "5_colored": + case "five_colored": + return color(tps()[1]); + case "15_colored": + case "fifteen_colored": + return color(tps()[2]); + } + return null; + } + +} diff --git a/target/PAPI-Expansion-Server.jar b/target/PAPI-Expansion-Server.jar index 5a32a3420e6eccf678f5c7acbe5dd3f6503cee82..459065293aee9afcfa91874272bf2b219b745fbb 100644 GIT binary patch literal 7855 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 literal 7693 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