commit bfb42251462e37bab464a6896e12c3f6d4b49455 Author: BaileyCodes Date: Sun Sep 20 17:18:50 2026 -0500 Initial commit: ET Placeholders plugin diff --git a/index.html b/index.html new file mode 100644 index 0000000..e742cd8 --- /dev/null +++ b/index.html @@ -0,0 +1,56 @@ + + + + + + ET Placeholders Wiki + + + + +

ET Placeholders Wiki

+

A PlaceholderAPI expansion for fetching player tiers and leaderboards from the EaglerTiers API.

+ +

Installation

+
    +
  1. Ensure you have PlaceholderAPI installed on your server.
  2. +
  3. Drop the ETPlaceholders-1.0.0.jar into your plugins folder.
  4. +
  5. Restart your server.
  6. +
  7. Configure your api-key in plugins/ETPlaceholders/config.yml (Optional but highly recommended to avoid rate limits).
  8. +
  9. Run /papi reload or restart the server.
  10. +
+ +

Configuration (config.yml)

+
# ET Placeholders Configuration
+api-url: "https://api.eaglertiers.com/api"
+api-key: "YOUR_KEY_HERE"
+cache-refresh-minutes: 15
+not-found-text: "Unranked"
+ +

Available Placeholders

+ +

Player Data

+ + +

Leaderboard Data

+ + +

Supported Gamemodes

+

overall, vanilla, mace, axe, sword, smp, diamondsmp, uhc, pot, nethop, cart

+ + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8d56f76 --- /dev/null +++ b/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + + dev.bailey + ETPlaceholders + 1.0.0 + jar + + ETPlaceholders + PlaceholderAPI expansion for EaglerTiers + + + UTF-8 + 1.8 + 1.8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + + + + org.spigotmc + spigot-api + 1.12.2-R0.1-SNAPSHOT + provided + + + me.clip + placeholderapi + 2.11.6 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 1.8 + 1.8 + + + + + diff --git a/src/main/java/dev/bailey/etplaceholders/DataManager.java b/src/main/java/dev/bailey/etplaceholders/DataManager.java new file mode 100644 index 0000000..0dcfda0 --- /dev/null +++ b/src/main/java/dev/bailey/etplaceholders/DataManager.java @@ -0,0 +1,180 @@ +package dev.bailey.etplaceholders; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; + +public class DataManager { + + private final ETPlaceholdersPlugin plugin; + private final Gson gson = new Gson(); + + private final Map> leaderboardCache = new ConcurrentHashMap<>(); + + private final Map> leaderboardTierCache = new ConcurrentHashMap<>(); + + private final Map> playerTierCache = new ConcurrentHashMap<>(); + + private final Map playerRankCache = new ConcurrentHashMap<>(); + + private final String[] trackedModes = {"overall", "vanilla", "mace", "axe", "sword", "smp", "diamondsmp", "uhc", "pot", "nethop", "cart"}; + + public DataManager(ETPlaceholdersPlugin plugin) { + this.plugin = plugin; + } + + public void startTasks() { + int minutes = plugin.getConfig().getInt("cache-refresh-minutes", 15); + long ticks = minutes * 60 * 20L; + + Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this::refreshLeaderboards, 0L, ticks); + } + + private String getApiUrl() { + String url = plugin.getConfig().getString("api-url", "https://api.eaglertiers.com/api"); + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + return url; + } + + private HttpURLConnection createConnection(String endpoint) throws Exception { + String urlStr = getApiUrl() + endpoint; + String key = plugin.getConfig().getString("api-key", ""); + if (key != null && !key.trim().isEmpty()) { + if (urlStr.contains("?")) { + urlStr += "&key=" + key; + } else { + urlStr += "?key=" + key; + } + } + + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + + if (key != null && !key.trim().isEmpty()) { + conn.setRequestProperty("X-API-Key", key); + } + + return conn; + } + + public void refreshLeaderboards() { + for (String mode : trackedModes) { + try { + String endpoint = mode.equalsIgnoreCase("overall") ? "/players" : "/rankings/" + mode; + HttpURLConnection conn = createConnection(endpoint); + + if (conn.getResponseCode() == 200) { + try (InputStreamReader reader = new InputStreamReader(conn.getInputStream())) { + JsonObject json = gson.fromJson(reader, JsonObject.class); + if (json != null && json.has("data")) { + JsonArray data = json.getAsJsonArray("data"); + Map modeTopNames = new ConcurrentHashMap<>(); + Map modeTopTiers = new ConcurrentHashMap<>(); + + for (int i = 0; i < data.size(); i++) { + JsonObject pObj = data.get(i).getAsJsonObject(); + String name = pObj.has("username") && !pObj.get("username").isJsonNull() ? pObj.get("username").getAsString() : "Unknown"; + int pos = pObj.has("position") && !pObj.get("position").isJsonNull() ? pObj.get("position").getAsInt() : (i + 1); + + modeTopNames.put(pos, name); + + String tier = plugin.getConfig().getString("not-found-text", "Unranked"); + if (pObj.has("tiers") && !pObj.get("tiers").isJsonNull()) { + JsonObject tiersObj = pObj.getAsJsonObject("tiers"); + if (tiersObj.has(mode) && !tiersObj.get(mode).isJsonNull()) { + tier = tiersObj.get(mode).getAsString(); + } + } + modeTopTiers.put(pos, tier); + } + leaderboardCache.put(mode.toLowerCase(), modeTopNames); + leaderboardTierCache.put(mode.toLowerCase(), modeTopTiers); + } + } + } + } catch (Exception e) { + plugin.getLogger().warning("Failed to fetch leaderboard for " + mode + ": " + e.getMessage()); + } + } + } + + public void fetchPlayerAsync(String playerName) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + try { + HttpURLConnection conn = createConnection("/players/" + playerName); + if (conn.getResponseCode() == 200) { + try (InputStreamReader reader = new InputStreamReader(conn.getInputStream())) { + JsonObject json = gson.fromJson(reader, JsonObject.class); + if (json != null) { + Map pTiers = new ConcurrentHashMap<>(); + + if (json.has("tiers") && !json.get("tiers").isJsonNull()) { + JsonObject tiersObj = json.getAsJsonObject("tiers"); + for (Map.Entry entry : tiersObj.entrySet()) { + if (!entry.getValue().isJsonNull()) { + pTiers.put(entry.getKey().toLowerCase(), entry.getValue().getAsString()); + } + } + } + + playerTierCache.put(playerName.toLowerCase(), pTiers); + + if (json.has("position") && !json.get("position").isJsonNull()) { + playerRankCache.put(playerName.toLowerCase(), json.get("position").getAsInt()); + } + } + } + } + } catch (Exception e) { + } + }); + } + + public String getPlayerTier(String playerName, String mode) { + String pLow = playerName.toLowerCase(); + if (!playerTierCache.containsKey(pLow)) { + fetchPlayerAsync(playerName); + return plugin.getConfig().getString("not-found-text", "Unranked"); + } + Map tiers = playerTierCache.get(pLow); + return tiers.getOrDefault(mode.toLowerCase(), plugin.getConfig().getString("not-found-text", "Unranked")); + } + + public String getPlayerRank(String playerName) { + String pLow = playerName.toLowerCase(); + if (!playerRankCache.containsKey(pLow)) { + fetchPlayerAsync(playerName); + return plugin.getConfig().getString("not-found-text", "Unranked"); + } + return String.valueOf(playerRankCache.get(pLow)); + } + + public String getTopPlayerName(String mode, int position) { + Map modeCache = leaderboardCache.get(mode.toLowerCase()); + if (modeCache == null || !modeCache.containsKey(position)) { + return plugin.getConfig().getString("not-found-text", "None"); + } + return modeCache.get(position); + } + + public String getTopPlayerTier(String mode, int position) { + Map modeCache = leaderboardTierCache.get(mode.toLowerCase()); + if (modeCache == null || !modeCache.containsKey(position)) { + return plugin.getConfig().getString("not-found-text", "Unranked"); + } + return modeCache.get(position); + } +} diff --git a/src/main/java/dev/bailey/etplaceholders/ETExpansion.java b/src/main/java/dev/bailey/etplaceholders/ETExpansion.java new file mode 100644 index 0000000..bde2082 --- /dev/null +++ b/src/main/java/dev/bailey/etplaceholders/ETExpansion.java @@ -0,0 +1,75 @@ +package dev.bailey.etplaceholders; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.entity.Player; + +public class ETExpansion extends PlaceholderExpansion { + + private final ETPlaceholdersPlugin plugin; + + public ETExpansion(ETPlaceholdersPlugin plugin) { + this.plugin = plugin; + } + + @Override + public String getIdentifier() { + return "et"; + } + + @Override + public String getAuthor() { + return "Bailey"; + } + + @Override + public String getVersion() { + return plugin.getDescription().getVersion(); + } + + @Override + public boolean persist() { + return true; + } + + @Override + public String onPlaceholderRequest(Player player, String identifier) { + if (identifier.equalsIgnoreCase("rank")) { + if (player == null) return ""; + return plugin.getDataManager().getPlayerRank(player.getName()); + } + + if (identifier.toLowerCase().startsWith("tier_")) { + if (player == null) return ""; + String mode = identifier.substring(5); + return plugin.getDataManager().getPlayerTier(player.getName(), mode); + } + + if (identifier.toLowerCase().startsWith("top_name_")) { + String[] parts = identifier.split("_"); + if (parts.length == 4) { + String mode = parts[2]; + try { + int pos = Integer.parseInt(parts[3]); + return plugin.getDataManager().getTopPlayerName(mode, pos); + } catch (NumberFormatException e) { + return null; + } + } + } + + if (identifier.toLowerCase().startsWith("top_tier_")) { + String[] parts = identifier.split("_"); + if (parts.length == 4) { + String mode = parts[2]; + try { + int pos = Integer.parseInt(parts[3]); + return plugin.getDataManager().getTopPlayerTier(mode, pos); + } catch (NumberFormatException e) { + return null; + } + } + } + + return null; + } +} diff --git a/src/main/java/dev/bailey/etplaceholders/ETPlaceholdersPlugin.java b/src/main/java/dev/bailey/etplaceholders/ETPlaceholdersPlugin.java new file mode 100644 index 0000000..16b47f2 --- /dev/null +++ b/src/main/java/dev/bailey/etplaceholders/ETPlaceholdersPlugin.java @@ -0,0 +1,33 @@ +package dev.bailey.etplaceholders; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; + +public class ETPlaceholdersPlugin extends JavaPlugin { + + private DataManager dataManager; + + @Override + public void onEnable() { + saveDefaultConfig(); + + dataManager = new DataManager(this); + dataManager.startTasks(); + + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + new ETExpansion(this).register(); + getLogger().info("ETPlaceholders enabled and hooked into PlaceholderAPI."); + } else { + getLogger().warning("PlaceholderAPI not found! Placeholders will not work."); + } + } + + @Override + public void onDisable() { + Bukkit.getScheduler().cancelTasks(this); + } + + public DataManager getDataManager() { + return dataManager; + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..b27f73d --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,15 @@ +# ET Placeholders Configuration +# Available Gamemodes: overall, vanilla, mace, axe, sword, smp, diamondsmp, uhc, pot, nethop, cart + +# The base URL of the EaglerTiers API +api-url: "https://api.eaglertiers.com/api" + +# Your API key. While public read requests don't require an API key, it is highly recommended to provide one to avoid rate limits. +api-key: "" + +# How often (in minutes) to refresh cached player and leaderboard data. +# Do not set this too low or you may hit the API rate limit (120 req/min). +cache-refresh-minutes: 15 + +# Value to display when a tier or player is not found +not-found-text: "Unranked" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..1cb000b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: ETPlaceholders +version: 1.0.0 +main: dev.bailey.etplaceholders.ETPlaceholdersPlugin +api-version: "1.13" +softdepend: [PlaceholderAPI] +description: PlaceholderAPI expansion for EaglerTiers +author: Bailey diff --git a/target/ETPlaceholders-1.0.0.jar b/target/ETPlaceholders-1.0.0.jar new file mode 100644 index 0000000..9b966c8 Binary files /dev/null and b/target/ETPlaceholders-1.0.0.jar differ diff --git a/target/classes/config.yml b/target/classes/config.yml new file mode 100644 index 0000000..b27f73d --- /dev/null +++ b/target/classes/config.yml @@ -0,0 +1,15 @@ +# ET Placeholders Configuration +# Available Gamemodes: overall, vanilla, mace, axe, sword, smp, diamondsmp, uhc, pot, nethop, cart + +# The base URL of the EaglerTiers API +api-url: "https://api.eaglertiers.com/api" + +# Your API key. While public read requests don't require an API key, it is highly recommended to provide one to avoid rate limits. +api-key: "" + +# How often (in minutes) to refresh cached player and leaderboard data. +# Do not set this too low or you may hit the API rate limit (120 req/min). +cache-refresh-minutes: 15 + +# Value to display when a tier or player is not found +not-found-text: "Unranked" diff --git a/target/classes/dev/bailey/etplaceholders/DataManager.class b/target/classes/dev/bailey/etplaceholders/DataManager.class new file mode 100644 index 0000000..7ed886d Binary files /dev/null and b/target/classes/dev/bailey/etplaceholders/DataManager.class differ diff --git a/target/classes/dev/bailey/etplaceholders/ETExpansion.class b/target/classes/dev/bailey/etplaceholders/ETExpansion.class new file mode 100644 index 0000000..00b18b4 Binary files /dev/null and b/target/classes/dev/bailey/etplaceholders/ETExpansion.class differ diff --git a/target/classes/dev/bailey/etplaceholders/ETPlaceholdersPlugin.class b/target/classes/dev/bailey/etplaceholders/ETPlaceholdersPlugin.class new file mode 100644 index 0000000..c206ba5 Binary files /dev/null and b/target/classes/dev/bailey/etplaceholders/ETPlaceholdersPlugin.class differ diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml new file mode 100644 index 0000000..1cb000b --- /dev/null +++ b/target/classes/plugin.yml @@ -0,0 +1,7 @@ +name: ETPlaceholders +version: 1.0.0 +main: dev.bailey.etplaceholders.ETPlaceholdersPlugin +api-version: "1.13" +softdepend: [PlaceholderAPI] +description: PlaceholderAPI expansion for EaglerTiers +author: Bailey diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..10097c9 --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=ETPlaceholders +groupId=dev.bailey +version=1.0.0 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..f25342e --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,3 @@ +dev\bailey\etplaceholders\DataManager.class +dev\bailey\etplaceholders\ETExpansion.class +dev\bailey\etplaceholders\ETPlaceholdersPlugin.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..f1c76ae --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,3 @@ +X:\dev\plugin\et_placeholders\src\main\java\dev\bailey\etplaceholders\DataManager.java +X:\dev\plugin\et_placeholders\src\main\java\dev\bailey\etplaceholders\ETExpansion.java +X:\dev\plugin\et_placeholders\src\main\java\dev\bailey\etplaceholders\ETPlaceholdersPlugin.java