Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-05-10 15:07:07 +02:00
parent a32f087353
commit 81786dc084
2 changed files with 214 additions and 62 deletions

View File

@@ -2,12 +2,15 @@ package net.viper.statusapibridge;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@@ -28,7 +31,14 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
private String statusApiUrl; private String statusApiUrl;
private int pushDelayTicks; private int pushDelayTicks;
private int liveSyncIntervalTicks; private int liveSyncIntervalTicks;
private int scoreboardSyncIntervalTicks;
private final Map<UUID, Double> lastPushedBalance = new ConcurrentHashMap<>(); private final Map<UUID, Double> lastPushedBalance = new ConcurrentHashMap<>();
private final Map<UUID, Double> lastPushedHealth = new ConcurrentHashMap<>();
private final Map<UUID, String> lastPushedCompass = new ConcurrentHashMap<>();
private final Map<UUID, String> lastPushedWorld = new ConcurrentHashMap<>();
private final Map<UUID, String> lastPushedData = new ConcurrentHashMap<>();
private final ExecutorService httpExecutor = Executors.newSingleThreadExecutor(r -> { private final ExecutorService httpExecutor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "StatusAPIBridge-HTTP"); Thread t = new Thread(r, "StatusAPIBridge-HTTP");
t.setDaemon(true); t.setDaemon(true);
@@ -41,6 +51,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
statusApiUrl = getConfig().getString("statusapi-url", "http://127.0.0.1:9191").trim(); statusApiUrl = getConfig().getString("statusapi-url", "http://127.0.0.1:9191").trim();
pushDelayTicks = getConfig().getInt("push-delay-ticks", 40); pushDelayTicks = getConfig().getInt("push-delay-ticks", 40);
liveSyncIntervalTicks = Math.max(20, getConfig().getInt("live-sync-interval-ticks", 20)); liveSyncIntervalTicks = Math.max(20, getConfig().getInt("live-sync-interval-ticks", 20));
scoreboardSyncIntervalTicks = Math.max(20, getConfig().getInt("scoreboard-sync-interval-ticks", 20));
if (!setupEconomy()) { if (!setupEconomy()) {
getLogger().warning("Vault/Economy nicht gefunden Economy-Push deaktiviert."); getLogger().warning("Vault/Economy nicht gefunden Economy-Push deaktiviert.");
@@ -51,6 +62,9 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(this, this);
Bukkit.getScheduler().runTaskTimer(this, this::pushChangedBalancesForOnlinePlayers, Bukkit.getScheduler().runTaskTimer(this, this::pushChangedBalancesForOnlinePlayers,
liveSyncIntervalTicks, liveSyncIntervalTicks); liveSyncIntervalTicks, liveSyncIntervalTicks);
Bukkit.getScheduler().runTaskTimer(this, this::pushScoreboardData,
scoreboardSyncIntervalTicks, scoreboardSyncIntervalTicks);
getLogger().info("StatusAPIBridge gestartet. Ziel: " + statusApiUrl); getLogger().info("StatusAPIBridge gestartet. Ziel: " + statusApiUrl);
} }
@@ -61,70 +75,98 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
private boolean setupEconomy() { private boolean setupEconomy() {
if (getServer().getPluginManager().getPlugin("Vault") == null) return false; if (getServer().getPluginManager().getPlugin("Vault") == null) return false;
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class); RegisteredServiceProvider<Economy> rsp =
getServer().getServicesManager().getRegistration(Economy.class);
if (rsp == null) return false; if (rsp == null) return false;
economy = rsp.getProvider(); economy = rsp.getProvider();
return economy != null; return economy != null;
} }
// ── Events ──────────────────────────────────────────────────────────────── // ── Events ────────────────────────────────────────────────────────────────
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent e) { public void onJoin(PlayerJoinEvent e) {
Player player = e.getPlayer(); Player player = e.getPlayer();
// Welt sofort pushen
pushWorld(player.getUniqueId(), player.getName(), player.getWorld().getName());
// Economy verzögert pushen
if (economy != null) {
Bukkit.getScheduler().runTaskLater(this, () -> { Bukkit.getScheduler().runTaskLater(this, () -> {
if (player.isOnline()) pushEconomy(player); if (!player.isOnline()) return;
if (economy != null) pushEconomy(player);
pushPlayerScoreboardData(player);
}, pushDelayTicks); }, pushDelayTicks);
} }
}
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent e) { public void onQuit(PlayerQuitEvent e) {
Player player = e.getPlayer(); Player player = e.getPlayer();
if (economy != null) { UUID id = player.getUniqueId();
double balance = economy.getBalance(player); if (economy != null) pushEconomyAsync(id, player.getName(), economy.getBalance(player));
pushEconomyAsync(player.getUniqueId(), player.getName(), balance); lastPushedBalance.remove(id);
} lastPushedHealth.remove(id);
lastPushedBalance.remove(player.getUniqueId()); lastPushedCompass.remove(id);
lastPushedWorld.remove(id);
lastPushedData.remove(id);
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldChange(PlayerChangedWorldEvent e) { public void onDamage(EntityDamageEvent e) {
Player player = e.getPlayer(); if (!(e.getEntity() instanceof Player)) return;
pushWorld(player.getUniqueId(), player.getName(), player.getWorld().getName()); Player player = (Player) e.getEntity();
Bukkit.getScheduler().runTaskLater(this,
() -> { if (player.isOnline()) pushHealthIfChanged(player); }, 1L);
} }
// ── World-Push ───────────────────────────────────────────────────────────── @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onHeal(EntityRegainHealthEvent e) {
private void pushWorld(UUID uuid, String name, String world) { if (!(e.getEntity() instanceof Player)) return;
httpExecutor.execute(() -> { Player player = (Player) e.getEntity();
try { Bukkit.getScheduler().runTaskLater(this,
String json = "{\"uuid\":\"" + uuid + "\",\"name\":\"" + escape(name) () -> { if (player.isOnline()) pushHealthIfChanged(player); }, 1L);
+ "\",\"world\":\"" + escape(world) + "\"}";
sendPost(statusApiUrl + "/player/world", json);
} catch (Exception ex) {
getLogger().warning("World-Push fehlgeschlagen fuer " + name + ": " + ex.getMessage());
}
});
} }
// ── Economy-Push ─────────────────────────────────────────────────────────── @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent e) {
if (e.getFrom().getYaw() == e.getTo().getYaw()) return;
pushCompassIfChanged(e.getPlayer());
}
// ── Periodische Tasks ─────────────────────────────────────────────────────
private void pushChangedBalancesForOnlinePlayers() {
if (economy == null) return;
for (Player player : Bukkit.getOnlinePlayers()) {
double current = economy.getBalance(player);
Double last = lastPushedBalance.get(player.getUniqueId());
if (last == null || Math.abs(current - last) > 0.000001d)
pushEconomyAsync(player.getUniqueId(), player.getName(), current);
}
}
private void pushScoreboardData() {
double tps = getCurrentTps();
for (Player player : Bukkit.getOnlinePlayers()) {
pushPlayerScoreboardData(player);
pushTpsAsync(player.getUniqueId(), tps);
}
}
private void pushPlayerScoreboardData(Player player) {
pushHealthIfChanged(player);
pushCompassIfChanged(player);
pushWorldIfChanged(player);
pushPlayerDataIfChanged(player);
}
// ── Push-Methoden ─────────────────────────────────────────────────────────
public void pushEconomy(Player player) { public void pushEconomy(Player player) {
double balance = economy.getBalance(player); pushEconomyAsync(player.getUniqueId(), player.getName(), economy.getBalance(player));
pushEconomyAsync(player.getUniqueId(), player.getName(), balance);
} }
private void pushEconomyAsync(UUID uuid, String name, double balance) { private void pushEconomyAsync(UUID uuid, String name, double balance) {
httpExecutor.execute(() -> { httpExecutor.execute(() -> {
try { try {
String json = "{\"uuid\":\"" + uuid + "\",\"name\":\"" + escape(name) sendPost(statusApiUrl + "/economy/update",
+ "\",\"balance\":" + balance + "}"; "{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
sendPost(statusApiUrl + "/economy/update", json); + "\",\"balance\":" + balance + "}");
lastPushedBalance.put(uuid, balance); lastPushedBalance.put(uuid, balance);
} catch (Exception e) { } catch (Exception e) {
getLogger().warning("Economy-Push fehlgeschlagen fuer " + name + ": " + e.getMessage()); getLogger().warning("Economy-Push fehlgeschlagen fuer " + name + ": " + e.getMessage());
@@ -132,18 +174,127 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
}); });
} }
private void pushChangedBalancesForOnlinePlayers() { private void pushHealthIfChanged(Player player) {
if (economy == null) return; double health = player.getHealth();
for (Player player : Bukkit.getOnlinePlayers()) { Double last = lastPushedHealth.get(player.getUniqueId());
double current = economy.getBalance(player); if (last != null && Math.abs(health - last) < 0.01) return;
Double last = lastPushedBalance.get(player.getUniqueId()); lastPushedHealth.put(player.getUniqueId(), health);
if (last == null || Math.abs(current - last) > 0.000001d) { UUID uuid = player.getUniqueId(); String name = player.getName();
pushEconomyAsync(player.getUniqueId(), player.getName(), current); httpExecutor.execute(() -> {
} try {
sendPost(statusApiUrl + "/scoreboard/health",
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
+ "\",\"health\":" + health + "}");
} catch (Exception e) {
getLogger().warning("Health-Push fehlgeschlagen: " + e.getMessage());
} }
});
} }
// ── HTTP ─────────────────────────────────────────────────────────────────── private void pushCompassIfChanged(Player player) {
// Rohen Yaw normalisieren auf 0..360 (0 = Süden, wie MC-Konvention)
float rawYaw = player.getLocation().getYaw();
float normYaw = ((rawYaw % 360) + 360) % 360;
String yawStr = String.format(java.util.Locale.US, "%.1f", normYaw);
// Nur senden wenn Änderung >= 0.5° fein genug für 1-Grad-Slots
String lastStr = lastPushedCompass.get(player.getUniqueId());
if (lastStr != null) {
try {
float lastYaw = Float.parseFloat(lastStr);
float diff = Math.abs(normYaw - lastYaw);
if (diff > 180) diff = 360 - diff; // kürzester Bogenweg
if (diff < 0.5f) return;
} catch (NumberFormatException ignored) {}
}
lastPushedCompass.put(player.getUniqueId(), yawStr);
UUID uuid = player.getUniqueId(); String name = player.getName();
httpExecutor.execute(() -> {
try {
sendPost(statusApiUrl + "/scoreboard/compass",
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
+ "\",\"compass\":\"" + yawStr + "\"}");
} catch (Exception e) {
getLogger().warning("Compass-Push fehlgeschlagen: " + e.getMessage());
}
});
}
private void pushWorldIfChanged(Player player) {
String world = player.getWorld().getName();
if (world.equals(lastPushedWorld.get(player.getUniqueId()))) return;
lastPushedWorld.put(player.getUniqueId(), world);
UUID uuid = player.getUniqueId(); String name = player.getName();
httpExecutor.execute(() -> {
try {
sendPost(statusApiUrl + "/player/world",
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
+ "\",\"world\":\"" + escapeName(world) + "\"}");
} catch (Exception e) {
getLogger().warning("World-Push fehlgeschlagen: " + e.getMessage());
}
});
}
private void pushPlayerDataIfChanged(Player player) {
int x = player.getLocation().getBlockX();
int y = player.getLocation().getBlockY();
int z = player.getLocation().getBlockZ();
String gm = player.getGameMode().name();
int exp= player.getLevel();
int fd = player.getFoodLevel();
double sp = player.getWalkSpeed();
String wld= player.getWorld().getName();
String key = x+","+y+","+z+","+gm+","+exp+","+fd+","+String.format("%.2f",sp)+","+wld;
if (key.equals(lastPushedData.get(player.getUniqueId()))) return;
lastPushedData.put(player.getUniqueId(), key);
UUID uuid = player.getUniqueId();
String name = player.getName();
httpExecutor.execute(() -> {
try {
sendPost(statusApiUrl + "/player/data",
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
+ "\",\"x\":" + x
+ ",\"y\":" + y
+ ",\"z\":" + z
+ ",\"gamemode\":\"" + gm + "\""
+ ",\"exp\":" + exp
+ ",\"food\":" + fd
+ ",\"speed\":" + String.format(java.util.Locale.US, "%.4f", sp)
+ ",\"world\":\"" + escapeName(wld) + "\"}");
} catch (Exception e) {
getLogger().warning("PlayerData-Push fehlgeschlagen: " + e.getMessage());
}
});
}
private void pushTpsAsync(UUID uuid, double tps) {
httpExecutor.execute(() -> {
try {
sendPost(statusApiUrl + "/scoreboard/tps",
"{\"uuid\":\"" + uuid + "\",\"tps\":" + tps + "}");
} catch (Exception ignored) {}
});
}
// ── Hilfsmethoden ─────────────────────────────────────────────────────────
/** TPS Paper-API zuerst, dann Spigot-Reflection-Fallback */
private double getCurrentTps() {
try {
double[] tps = (double[]) Bukkit.getServer().getClass()
.getMethod("getTPS").invoke(Bukkit.getServer());
return Math.min(20.0, tps[0]);
} catch (Exception ignored) {}
try {
Object ms = Bukkit.getServer().getClass()
.getMethod("getServer").invoke(Bukkit.getServer());
double[] tps = (double[]) ms.getClass().getField("recentTps").get(ms);
return Math.min(20.0, tps[0]);
} catch (Exception ignored) {}
return 20.0;
}
private void sendPost(String urlStr, String json) throws Exception { private void sendPost(String urlStr, String json) throws Exception {
URL url = new URL(urlStr); URL url = new URL(urlStr);
@@ -155,18 +306,15 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
byte[] body = json.getBytes(StandardCharsets.UTF_8); byte[] body = json.getBytes(StandardCharsets.UTF_8);
conn.setRequestProperty("Content-Length", String.valueOf(body.length)); conn.setRequestProperty("Content-Length", String.valueOf(body.length));
try (OutputStream os = conn.getOutputStream()) { try (OutputStream os = conn.getOutputStream()) { os.write(body); }
os.write(body);
}
int code = conn.getResponseCode(); int code = conn.getResponseCode();
if (code != 200) { if (code != 200)
getLogger().warning("StatusAPI antwortete mit Code " + code + " fuer " + urlStr); getLogger().warning("StatusAPI antwortete mit Code " + code + " fuer " + urlStr);
}
conn.disconnect(); conn.disconnect();
} }
private String escape(String s) { private String escapeName(String name) {
if (s == null) return ""; if (name == null) return "";
return s.replace("\\", "\\\\").replace("\"", "\\\""); return name.replace("\\", "\\\\").replace("\"", "\\\"");
} }
} }

View File

@@ -4,5 +4,9 @@ statusapi-url: "http://127.0.0.1:9191"
# Wie viele Ticks nach dem Join wird die Balance gepusht? (20 Ticks = 1 Sekunde) # Wie viele Ticks nach dem Join wird die Balance gepusht? (20 Ticks = 1 Sekunde)
push-delay-ticks: 40 push-delay-ticks: 40
# Live-Sync Intervall fuer Economy-Updates waehrend der Spieler online ist (mind. 20 Ticks = 1 Sekunde) # Live-Sync Intervall fuer Economy-Updates waehrend der Spieler online ist (mind. 20 Ticks)
live-sync-interval-ticks: 20 live-sync-interval-ticks: 20
# Sync-Intervall fuer Scoreboard-Daten (Health, Compass, TPS, World) in Ticks (mind. 20)
# Compass und Health werden zusaetzlich event-basiert aktualisiert.
scoreboard-sync-interval-ticks: 20