diff --git a/StatusAPIBridge/pom.xml b/StatusAPIBridge/pom.xml
new file mode 100644
index 0000000..2ef98af
--- /dev/null
+++ b/StatusAPIBridge/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ net.viper
+ StatusAPIBridge
+ 1.0.0
+ jar
+
+
+ 17
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+ vault-repo
+ https://nexus.hc.to/content/repositories/pub_releases/
+
+
+
+
+
+
+ org.spigotmc
+ spigot-api
+ 1.21-R0.1-SNAPSHOT
+ provided
+
+
+
+ net.milkbowl.vault
+ VaultAPI
+ 1.7
+ provided
+
+
+
+
+ StatusAPIBridge
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+ shade
+
+ false
+
+
+
+
+
+
+
diff --git a/StatusAPIBridge/src/main/java/net/viper/statusapibridge/StatusAPIBridge.java b/StatusAPIBridge/src/main/java/net/viper/statusapibridge/StatusAPIBridge.java
new file mode 100644
index 0000000..986d299
--- /dev/null
+++ b/StatusAPIBridge/src/main/java/net/viper/statusapibridge/StatusAPIBridge.java
@@ -0,0 +1,172 @@
+package net.viper.statusapibridge;
+
+import net.milkbowl.vault.economy.Economy;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerChangedWorldEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.plugin.RegisteredServiceProvider;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class StatusAPIBridge extends JavaPlugin implements Listener {
+
+ private Economy economy;
+ private String statusApiUrl;
+ private int pushDelayTicks;
+ private int liveSyncIntervalTicks;
+ private final Map lastPushedBalance = new ConcurrentHashMap<>();
+ private final ExecutorService httpExecutor = Executors.newSingleThreadExecutor(r -> {
+ Thread t = new Thread(r, "StatusAPIBridge-HTTP");
+ t.setDaemon(true);
+ return t;
+ });
+
+ @Override
+ public void onEnable() {
+ saveDefaultConfig();
+ statusApiUrl = getConfig().getString("statusapi-url", "http://127.0.0.1:9191").trim();
+ pushDelayTicks = getConfig().getInt("push-delay-ticks", 40);
+ liveSyncIntervalTicks = Math.max(20, getConfig().getInt("live-sync-interval-ticks", 20));
+
+ if (!setupEconomy()) {
+ getLogger().warning("Vault/Economy nicht gefunden – Economy-Push deaktiviert.");
+ } else {
+ getLogger().info("Vault Economy gefunden: " + economy.getName());
+ }
+
+ Bukkit.getPluginManager().registerEvents(this, this);
+ Bukkit.getScheduler().runTaskTimer(this, this::pushChangedBalancesForOnlinePlayers,
+ liveSyncIntervalTicks, liveSyncIntervalTicks);
+ getLogger().info("StatusAPIBridge gestartet. Ziel: " + statusApiUrl);
+ }
+
+ @Override
+ public void onDisable() {
+ httpExecutor.shutdownNow();
+ }
+
+ private boolean setupEconomy() {
+ if (getServer().getPluginManager().getPlugin("Vault") == null) return false;
+ RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class);
+ if (rsp == null) return false;
+ economy = rsp.getProvider();
+ return economy != null;
+ }
+
+ // ── Events ─────────────────────────────────────────────────────────────────
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onJoin(PlayerJoinEvent e) {
+ 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, () -> {
+ if (player.isOnline()) pushEconomy(player);
+ }, pushDelayTicks);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onQuit(PlayerQuitEvent e) {
+ Player player = e.getPlayer();
+ if (economy != null) {
+ double balance = economy.getBalance(player);
+ pushEconomyAsync(player.getUniqueId(), player.getName(), balance);
+ }
+ lastPushedBalance.remove(player.getUniqueId());
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onWorldChange(PlayerChangedWorldEvent e) {
+ Player player = e.getPlayer();
+ pushWorld(player.getUniqueId(), player.getName(), player.getWorld().getName());
+ }
+
+ // ── World-Push ─────────────────────────────────────────────────────────────
+
+ private void pushWorld(UUID uuid, String name, String world) {
+ httpExecutor.execute(() -> {
+ try {
+ String json = "{\"uuid\":\"" + uuid + "\",\"name\":\"" + escape(name)
+ + "\",\"world\":\"" + escape(world) + "\"}";
+ sendPost(statusApiUrl + "/player/world", json);
+ } catch (Exception ex) {
+ getLogger().warning("World-Push fehlgeschlagen fuer " + name + ": " + ex.getMessage());
+ }
+ });
+ }
+
+ // ── Economy-Push ───────────────────────────────────────────────────────────
+
+ public void pushEconomy(Player player) {
+ double balance = economy.getBalance(player);
+ pushEconomyAsync(player.getUniqueId(), player.getName(), balance);
+ }
+
+ private void pushEconomyAsync(UUID uuid, String name, double balance) {
+ httpExecutor.execute(() -> {
+ try {
+ String json = "{\"uuid\":\"" + uuid + "\",\"name\":\"" + escape(name)
+ + "\",\"balance\":" + balance + "}";
+ sendPost(statusApiUrl + "/economy/update", json);
+ lastPushedBalance.put(uuid, balance);
+ } catch (Exception e) {
+ getLogger().warning("Economy-Push fehlgeschlagen fuer " + name + ": " + e.getMessage());
+ }
+ });
+ }
+
+ 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);
+ }
+ }
+ }
+
+ // ── HTTP ───────────────────────────────────────────────────────────────────
+
+ private void sendPost(String urlStr, String json) throws Exception {
+ URL url = new URL(urlStr);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("POST");
+ conn.setDoOutput(true);
+ conn.setConnectTimeout(3000);
+ conn.setReadTimeout(3000);
+ conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+ byte[] body = json.getBytes(StandardCharsets.UTF_8);
+ conn.setRequestProperty("Content-Length", String.valueOf(body.length));
+ try (OutputStream os = conn.getOutputStream()) {
+ os.write(body);
+ }
+ int code = conn.getResponseCode();
+ if (code != 200) {
+ getLogger().warning("StatusAPI antwortete mit Code " + code + " fuer " + urlStr);
+ }
+ conn.disconnect();
+ }
+
+ private String escape(String s) {
+ if (s == null) return "";
+ return s.replace("\\", "\\\\").replace("\"", "\\\"");
+ }
+}
diff --git a/StatusAPIBridge/src/main/resources/config.yml b/StatusAPIBridge/src/main/resources/config.yml
new file mode 100644
index 0000000..be2cd64
--- /dev/null
+++ b/StatusAPIBridge/src/main/resources/config.yml
@@ -0,0 +1,8 @@
+# URL der BungeeCord StatusAPI (kein Slash am Ende)
+statusapi-url: "http://127.0.0.1:9191"
+
+# Wie viele Ticks nach dem Join wird die Balance gepusht? (20 Ticks = 1 Sekunde)
+push-delay-ticks: 40
+
+# Live-Sync Intervall fuer Economy-Updates waehrend der Spieler online ist (mind. 20 Ticks = 1 Sekunde)
+live-sync-interval-ticks: 20
diff --git a/StatusAPIBridge/src/main/resources/plugin.yml b/StatusAPIBridge/src/main/resources/plugin.yml
new file mode 100644
index 0000000..694d0ae
--- /dev/null
+++ b/StatusAPIBridge/src/main/resources/plugin.yml
@@ -0,0 +1,7 @@
+name: StatusAPIBridge
+version: 1.0.0
+main: net.viper.statusapibridge.StatusAPIBridge
+api-version: 1.21
+description: Sendet Vault-Economy-Daten an die BungeeCord StatusAPI
+authors: [Viper]
+softdepend: [Vault]