Initial commit: ET Placeholders plugin
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ET Placeholders Wiki</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 20px; color: #333; }
|
||||
h1, h2, h3 { color: #2c3e50; border-bottom: 1px solid #eee; padding-bottom: 5px; }
|
||||
code { background: #f4f4f4; padding: 2px 5px; border-radius: 3px; font-family: monospace; }
|
||||
pre { background: #f4f4f4; padding: 15px; border-radius: 5px; overflow-x: auto; }
|
||||
.placeholder-list { list-style: none; padding: 0; }
|
||||
.placeholder-list li { margin-bottom: 10px; background: #fff; padding: 10px; border: 1px solid #ddd; border-radius: 5px; }
|
||||
.placeholder-list code { font-weight: bold; color: #e74c3c; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ET Placeholders Wiki</h1>
|
||||
<p>A PlaceholderAPI expansion for fetching player tiers and leaderboards from the EaglerTiers API.</p>
|
||||
|
||||
<h2>Installation</h2>
|
||||
<ol>
|
||||
<li>Ensure you have <strong>PlaceholderAPI</strong> installed on your server.</li>
|
||||
<li>Drop the <code>ETPlaceholders-1.0.0.jar</code> into your <code>plugins</code> folder.</li>
|
||||
<li>Restart your server.</li>
|
||||
<li>Configure your <code>api-key</code> in <code>plugins/ETPlaceholders/config.yml</code> (Optional but highly recommended to avoid rate limits).</li>
|
||||
<li>Run <code>/papi reload</code> or restart the server.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Configuration (config.yml)</h2>
|
||||
<pre><code># ET Placeholders Configuration
|
||||
api-url: "https://api.eaglertiers.com/api"
|
||||
api-key: "YOUR_KEY_HERE"
|
||||
cache-refresh-minutes: 15
|
||||
not-found-text: "Unranked"</code></pre>
|
||||
|
||||
<h2>Available Placeholders</h2>
|
||||
|
||||
<h3>Player Data</h3>
|
||||
<ul class="placeholder-list">
|
||||
<li><code>%et_rank%</code><br>Returns the player's overall rank position on EaglerTiers.</li>
|
||||
<li><code>%et_tier_<gamemode>%</code><br>Returns the player's tier for a specific gamemode. Example: <code>%et_tier_vanilla%</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Leaderboard Data</h3>
|
||||
<ul class="placeholder-list">
|
||||
<li><code>%et_top_name_<gamemode>_<position>%</code><br>Returns the username of the player at the specified leaderboard position. Example: <code>%et_top_name_overall_1%</code></li>
|
||||
<li><code>%et_top_tier_<gamemode>_<position>%</code><br>Returns the tier of the player at the specified leaderboard position. Example: <code>%et_top_tier_vanilla_3%</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Supported Gamemodes</h3>
|
||||
<p><code>overall</code>, <code>vanilla</code>, <code>mace</code>, <code>axe</code>, <code>sword</code>, <code>smp</code>, <code>diamondsmp</code>, <code>uhc</code>, <code>pot</code>, <code>nethop</code>, <code>cart</code></p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,58 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>dev.bailey</groupId>
|
||||
<artifactId>ETPlaceholders</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ETPlaceholders</name>
|
||||
<description>PlaceholderAPI expansion for EaglerTiers</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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<String, Map<Integer, String>> leaderboardCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Map<Integer, String>> leaderboardTierCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Map<String, String>> playerTierCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Integer> 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<Integer, String> modeTopNames = new ConcurrentHashMap<>();
|
||||
Map<Integer, String> 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<String, String> pTiers = new ConcurrentHashMap<>();
|
||||
|
||||
if (json.has("tiers") && !json.get("tiers").isJsonNull()) {
|
||||
JsonObject tiersObj = json.getAsJsonObject("tiers");
|
||||
for (Map.Entry<String, JsonElement> 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<String, String> 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<Integer, String> 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<Integer, String> modeCache = leaderboardTierCache.get(mode.toLowerCase());
|
||||
if (modeCache == null || !modeCache.containsKey(position)) {
|
||||
return plugin.getConfig().getString("not-found-text", "Unranked");
|
||||
}
|
||||
return modeCache.get(position);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
artifactId=ETPlaceholders
|
||||
groupId=dev.bailey
|
||||
version=1.0.0
|
||||
@@ -0,0 +1,3 @@
|
||||
dev\bailey\etplaceholders\DataManager.class
|
||||
dev\bailey\etplaceholders\ETExpansion.class
|
||||
dev\bailey\etplaceholders\ETPlaceholdersPlugin.class
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user