7 Commits
1.0 ... 1.4

4 changed files with 1086 additions and 878 deletions

23
pom.xml
View File

@@ -6,7 +6,7 @@
<groupId>de.m-viper</groupId>
<artifactId>PlayerStatusSign-reloaded</artifactId>
<version>1.2</version>
<version>1.4</version>
<packaging>jar</packaging>
<name>PlayerStatusSign</name>
@@ -25,16 +25,26 @@
</repositories>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- bStats Bukkit -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -44,10 +54,12 @@
<target>17</target>
</configuration>
</plugin>
<!-- Shade Plugin mit Relocation von bStats -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
@@ -56,11 +68,18 @@
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>de.m.viper.shaded.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>

View File

@@ -1,3 +1,5 @@
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
@@ -15,7 +17,10 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
@@ -26,8 +31,12 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Scanner;
import java.util.function.Consumer;
public class PlayerStatusSign extends JavaPlugin implements Listener {
@@ -56,6 +65,14 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
private static final String CONFIG_VERSION = "1.0.6";
private static final String PLUGIN_AUTHOR = "M_Viper";
// bStats Metrics-Objekt
private static final int BSTATS_PLUGIN_ID = 26877; // <-- bStats Service-ID hier eintragen!
private Metrics metrics;
// Neue Variablen zum Speichern des Update-Status
private boolean updateAvailable = false;
private String latestVersion = null;
// ------------------------------------------------------------
// Lifecycle
// ------------------------------------------------------------
@@ -75,6 +92,34 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
loadPlayers();
startSignUpdater();
// ---- bStats starten ----
metrics = new Metrics(this, BSTATS_PLUGIN_ID);
getLogger().info("bStats wurde gestartet (Plugin-ID: " + BSTATS_PLUGIN_ID + ")");
// ---- Spigot Update Checker ----
int resourceId = 127335; // Ersetze dies mit der ID deiner Plugin-Ressource auf SpigotMC
new UpdateChecker(this, resourceId).getVersion(version -> {
if (this.getDescription().getVersion().equals(version)) {
getLogger().info("Das Plugin ist auf dem neuesten Stand.");
updateAvailable = false;
latestVersion = null;
} else {
getLogger().info("Eine neue Plugin-Version ist verfügbar: " + version);
updateAvailable = true;
latestVersion = version;
// Sofort Nachricht an alle Operatoren oder “statussign.admin”-Permission
Bukkit.getScheduler().runTask(this, () -> {
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.isOp() || player.hasPermission("statussign.admin")) {
player.sendMessage(ChatColor.GREEN + "Es ist eine neue Version von PlayerStatusSign verfügbar: " + ChatColor.GOLD + version);
player.sendMessage(ChatColor.GREEN + "Lade sie auf SpigotMC herunter!");
}
}
});
}
});
// Konsolenmeldung beim Plugin-Start
getLogger().info("[>] ========================================== [<]");
getLogger().info(" PlayerStatusSign - " + getDescription().getVersion() + " (cfg " + CONFIG_VERSION + ")");
@@ -107,6 +152,47 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
savePlayers();
}
// Spieler-Join Event für Ingame Update-Nachricht
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (updateAvailable && (player.isOp() || player.hasPermission("statussign.admin"))) {
player.sendMessage(ChatColor.GREEN + "Es ist eine neue Version von PlayerStatusSign verfügbar: " + ChatColor.GOLD + latestVersion);
player.sendMessage(ChatColor.GREEN + "Lade sie auf SpigotMC herunter!");
}
}
// ... (restliche Methoden wie loadConfig, EventHandler etc. hier weiterhin einfügen) ...
// ------------------------------------------------------------
// UpdateChecker Klasse (innerhalb derselben Datei oder als separate Datei)
// ------------------------------------------------------------
public static class UpdateChecker {
private final JavaPlugin plugin;
private final int resourceId;
public UpdateChecker(JavaPlugin plugin, int resourceId) {
this.plugin = plugin;
this.resourceId = resourceId;
}
public void getVersion(final Consumer<String> consumer) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + resourceId).openStream();
Scanner scanner = new Scanner(is)) {
if (scanner.hasNext()) {
consumer.accept(scanner.next());
}
} catch (IOException e) {
plugin.getLogger().warning("Unable to check for updates: " + e.getMessage());
}
});
}
}
// ------------------------------------------------------------
// Config / Storage
// ------------------------------------------------------------
@@ -411,12 +497,12 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
// ------------------------------------------------------------
// GUI
// ------------------------------------------------------------
private void openStatsGUI(Player viewer, String targetPlayerName) {
// GUI
// ------------------------------------------------------------
private void openStatsGUI(Player viewer, String targetPlayerName) {
try {
Player target = getServer().getPlayerExact(targetPlayerName);
Inventory gui = getServer().createInventory(null, 9, ChatColor.translateAlternateColorCodes('&', "&6Statistiken von " + targetPlayerName));
Inventory gui = getServer().createInventory(null, 9, ChatColor.translateAlternateColorCodes('&', "&3Statistiken von " + targetPlayerName));
long playTimeTicks = 0;
int deaths = 0;
@@ -444,9 +530,10 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
distanceWalkedCm = playersConfig.getDouble("players." + targetPlayerName + ".stats.distance-walked", 0);
}
double playTimeHours = playTimeTicks / 72000.0; // 20 Ticks * 3600 Sekunden
double playTimeHours = playTimeTicks / 72000.0;
double distanceWalkedKm = distanceWalkedCm / 100000.0;
// Spielzeit
ItemStack playTimeItem = new ItemStack(Material.CLOCK);
ItemMeta playTimeMeta = playTimeItem.getItemMeta();
if (playTimeMeta != null) {
@@ -456,6 +543,7 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
gui.setItem(0, playTimeItem);
// Tode
ItemStack deathsItem = new ItemStack(Material.SKELETON_SKULL);
ItemMeta deathsMeta = deathsItem.getItemMeta();
if (deathsMeta != null) {
@@ -465,6 +553,7 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
gui.setItem(1, deathsItem);
// Kills
ItemStack killsItem = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta killsMeta = killsItem.getItemMeta();
if (killsMeta != null) {
@@ -474,6 +563,7 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
gui.setItem(2, killsItem);
// Abgebaute Blöcke
ItemStack blocksMinedItem = new ItemStack(Material.IRON_PICKAXE);
ItemMeta blocksMinedMeta = blocksMinedItem.getItemMeta();
if (blocksMinedMeta != null) {
@@ -483,6 +573,7 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
gui.setItem(3, blocksMinedItem);
// Gelaufene Distanz
ItemStack distanceItem = new ItemStack(Material.COMPASS);
ItemMeta distanceMeta = distanceItem.getItemMeta();
if (distanceMeta != null) {
@@ -492,12 +583,94 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
}
gui.setItem(4, distanceItem);
// NEU 5: Geflogene Distanz
double flewDistanceCm = (target != null && target.isOnline()) ?
target.getStatistic(Statistic.FLY_ONE_CM) :
playersConfig.getDouble("players." + targetPlayerName + ".stats.flew-distance", 0);
double flewDistanceKm = flewDistanceCm / 100000.0;
ItemStack flewDistanceItem = new ItemStack(Material.ELYTRA);
ItemMeta flewMeta = flewDistanceItem.getItemMeta();
if (flewMeta != null) {
flewMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&9Geflogene Distanz"));
flewMeta.setLore(Collections.singletonList(ChatColor.translateAlternateColorCodes('&', String.format("&7%.2f km", flewDistanceKm))));
flewDistanceItem.setItemMeta(flewMeta);
}
gui.setItem(5, flewDistanceItem);
// NEU 6: Crafting
int craftedCount = 0;
if (target != null && target.isOnline()) {
for (Material m : Material.values()) {
if (m.isItem()) {
try { craftedCount += target.getStatistic(Statistic.CRAFT_ITEM, m); } catch (Exception ignored) {}
}
}
} else {
craftedCount = playersConfig.getInt("players." + targetPlayerName + ".stats.crafted-items", 0);
}
ItemStack craftedItem = new ItemStack(Material.CRAFTING_TABLE);
ItemMeta craftedMeta = craftedItem.getItemMeta();
if (craftedMeta != null) {
craftedMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&eItems hergestellt"));
craftedMeta.setLore(Collections.singletonList(ChatColor.translateAlternateColorCodes('&', "&7" + craftedCount)));
craftedItem.setItemMeta(craftedMeta);
}
gui.setItem(6, craftedItem);
// NEU 7: Blöcke gesetzt
int blocksPlaced = 0;
if (target != null && target.isOnline()) {
for (Material m : Material.values()) {
if (m.isBlock()) {
try { blocksPlaced += target.getStatistic(Statistic.USE_ITEM, m); } catch (Exception ignored) {}
}
}
} else {
blocksPlaced = playersConfig.getInt("players." + targetPlayerName + ".stats.blocks-placed", 0);
}
ItemStack placedItem = new ItemStack(Material.OAK_PLANKS);
ItemMeta placedMeta = placedItem.getItemMeta();
if (placedMeta != null) {
placedMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&aGesetzte Blöcke"));
placedMeta.setLore(Collections.singletonList(ChatColor.translateAlternateColorCodes('&', "&7" + blocksPlaced)));
placedItem.setItemMeta(placedMeta);
}
gui.setItem(7, placedItem);
// NEU 8: Zeit seit letztem Tod
long lastDeathMs;
if (target != null && target.isOnline()) {
lastDeathMs = System.currentTimeMillis() - (target.getStatistic(Statistic.TIME_SINCE_DEATH) * 50L);
} else {
long recordedTime = playersConfig.getLong("players." + targetPlayerName + ".stats.last-death-time", System.currentTimeMillis());
lastDeathMs = System.currentTimeMillis() - recordedTime;
}
// Zeit seit letztem Tod in Stunden
if (target != null && target.isOnline()) {
lastDeathMs = System.currentTimeMillis() - (target.getStatistic(Statistic.TIME_SINCE_DEATH) * 50L);
} else {
long recordedTime = playersConfig.getLong("players." + targetPlayerName + ".stats.last-death-time", System.currentTimeMillis());
lastDeathMs = System.currentTimeMillis() - recordedTime;
}
double hoursSinceDeath = lastDeathMs / 3600000.0; // Millisekunden → Stunden
ItemStack deathItem = new ItemStack(Material.CLOCK);
ItemMeta deathMeta = deathItem.getItemMeta();
if (deathMeta != null) {
deathMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&6Zeit seit letztem Tod"));
deathMeta.setLore(Collections.singletonList(ChatColor.translateAlternateColorCodes('&',
String.format("&7%.2f Stunden", hoursSinceDeath))));
deathItem.setItemMeta(deathMeta);
}
gui.setItem(8, deathItem);
viewer.openInventory(gui);
} catch (Exception e) {
getLogger().warning("Fehler beim Öffnen der Statistik-GUI für " + targetPlayerName + ": " + e.getMessage());
viewer.sendMessage(getMessage("gui-error"));
}
}
}
// ------------------------------------------------------------
// Commands
@@ -653,6 +826,22 @@ public class PlayerStatusSign extends JavaPlugin implements Listener {
event.setCancelled(true);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
String title = ChatColor.stripColor(event.getView().getTitle());
if (title != null && title.startsWith("Statistiken von ")) {
event.setCancelled(true); // Verhindert Herausnehmen/Verschieben von Items im Stats-GUI
}
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
String title = ChatColor.stripColor(event.getView().getTitle());
if (title != null && title.startsWith("Statistiken von ")) {
event.setCancelled(true); // Verhindert Drag & Drop im Stats-GUI
}
}
@EventHandler(priority = EventPriority.LOW)
public void onBlockBreak(BlockBreakEvent event) {
if (event.isCancelled()) return;

View File

@@ -1,4 +1,4 @@
version: 1.0.6
version: 1.2
prefix: Status
colors:
online: "&a" # Grün

View File

@@ -1,6 +1,6 @@
name: PlayerStatusSign
main: PlayerStatusSign
version: 1.0.6
version: 1.4
author: M_Viper
api-version: 1.21
description: Zeigt Spielerstatus (online, AFK, offline) auf Schildern und erlaubt GUI-Statistikanzeige.