Compare commits

..

8 Commits
1.0 ... prod

Author SHA1 Message Date
bensuisse
df9f53b1af color fixed 2026-08-05 22:24:09 +02:00
bensuisse
fab579d5af added scoreboard 2026-08-05 22:21:52 +02:00
bensuisse
dc92ac9c4c removed 2026 lvl 2026-08-04 19:14:46 +02:00
bensuisse
5d9e0ba822 fix: removed ping of players 2026-08-04 18:05:04 +02:00
bensuisse
2e3a4315c2 feature: added tps and ping in tab 2026-08-04 17:59:48 +02:00
bensuisse
d6991a30b0 fix: changed colors 2026-08-03 22:57:07 +02:00
bensuisse
62f9d98030 fix: version to 1.1 2026-08-03 22:47:57 +02:00
bensuisse
6e6dee95b6 fix: changed lunara to miningman 2026-08-03 22:46:19 +02:00
27 changed files with 501 additions and 134 deletions

43
.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# Maven build output
target/
dependency-reduced-pom.xml
# Compiled class files
*.class
# Packaged archives
*.jar
*.war
*.ear
# Logs
*.log
hs_err_pid*
replay_pid*
# IntelliJ IDEA
*.iml
*.ipr
*.iws
.idea/shelf/
.idea/workspace.xml
.idea/usage.statistics.xml
.idea/dictionaries/
.idea/httpRequests/
.idea/dataSources/
.idea/dataSources.local.xml
# Eclipse
.classpath
.project
.settings/
# VS Code
.vscode/
# OS files
.DS_Store
Thumbs.db
# Environment / secrets
.env

View File

@ -7,6 +7,7 @@
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="LunaraSystem" />
<module name="MiningMan" />
</profile>
</annotationProcessing>
</component>

View File

@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/LunaraSystem.iml" filepath="$PROJECT_DIR$/LunaraSystem.iml" />
<module fileurl="file://$PROJECT_DIR$/MiningMan.iml" filepath="$PROJECT_DIR$/MiningMan.iml" />
</modules>
</component>
</project>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>PAPER</platformType>
</autoDetectTypes>
<projectReimportVersion>1</projectReimportVersion>
</configuration>
</facet>
</component>
</module>

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ch.bensuisse</groupId>
<artifactId>LunaraSystem</artifactId>
<name>LunaraSystem</name>
<version>1.0</version>
<build>
<defaultGoal>clean package</defaultGoal>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -6,7 +6,7 @@
<groupId>ch.bensuisse</groupId>
<artifactId>MiningMan</artifactId>
<version>1.0</version>
<version>2.0</version>
<packaging>jar</packaging>
<name>MiningMan</name>

View File

@ -5,6 +5,8 @@ import ch.bensuisse.miningman.features.announce.AnnounceCommand;
import ch.bensuisse.miningman.features.chat.ChatListener;
import ch.bensuisse.miningman.features.chat.ClearChatCommand;
import ch.bensuisse.miningman.features.general.ConnectionListener;
import ch.bensuisse.miningman.features.scoreboard.ScoreboardCommand;
import ch.bensuisse.miningman.features.scoreboard.ScoreboardService;
import ch.bensuisse.miningman.features.tablist.TablistService;
import ch.bensuisse.miningman.managers.JoinManager;
import net.luckperms.api.LuckPerms;
@ -16,6 +18,7 @@ public final class MiningMan extends JavaPlugin {
private LuckPerms luckPerms;
private TablistService tablistService;
private ScoreboardService scoreboardService;
@Override
public void onEnable() {
@ -26,6 +29,7 @@ public final class MiningMan extends JavaPlugin {
}
this.tablistService = new TablistService(this, luckPerms);
this.scoreboardService = new ScoreboardService(this, luckPerms);
registerListeners();
registerCommands();
@ -38,6 +42,8 @@ public final class MiningMan extends JavaPlugin {
public void onDisable() {
if (tablistService != null)
tablistService.onDisable();
if (scoreboardService != null)
scoreboardService.onDisable();
}
private boolean setupLuckPerms() {
@ -55,6 +61,7 @@ public final class MiningMan extends JavaPlugin {
pm.registerEvents(new ConnectionListener(luckPerms), this);
pm.registerEvents(new ChatListener(this), this);
pm.registerEvents(this.tablistService, this);
pm.registerEvents(this.scoreboardService, this);
}
private void registerCommands() {
@ -71,9 +78,17 @@ public final class MiningMan extends JavaPlugin {
ClearChatCommand clearChatCommand = new ClearChatCommand();
getCommand("clearchat").setExecutor(clearChatCommand);
getCommand("clearchat").setTabCompleter(clearChatCommand);
ScoreboardCommand scoreboardCommand = new ScoreboardCommand(scoreboardService);
getCommand("sb").setExecutor(scoreboardCommand);
getCommand("sb").setTabCompleter(scoreboardCommand);
}
public TablistService getTablistService() {
return tablistService;
}
public ScoreboardService getScoreboardService() {
return scoreboardService;
}
}

View File

@ -17,7 +17,7 @@ import java.util.List;
public class SocialCommands implements CommandExecutor, TabCompleter {
private static final TextColor THEME_COLOR = TextColor.fromHexString("#56CF94");
private static final TextColor THEME_COLOR = TextColor.fromHexString("#FFA80F");
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@ -36,13 +36,13 @@ public class SocialCommands implements CommandExecutor, TabCompleter {
private void sendDiscordMessage(Player player) {
Component message = Component.text()
.append(Component.newline())
.append(Component.text("ᴊᴏɪɴ ᴛʜᴇ ʟᴜɴᴀʀᴀᴍᴄ ᴅɪsᴄᴏʀᴅ ᴄᴏᴍᴍᴜɴɪᴛʏ!")
.append(Component.text("ᴊᴏɪɴ ᴛʜᴇ ᴍɪɴɪɴɢᴍᴀɴ ᴅɪsᴄᴏʀᴅ ᴄᴏᴍᴍᴜɴɪᴛʏ!")
.color(THEME_COLOR))
.append(Component.newline())
.append(Component.text("discord.gg/lunaramc")
.append(Component.text("discord.gg/V32MCYsFhn")
.color(TextColor.color(255, 255, 255))
.decorate(TextDecoration.UNDERLINED)
.clickEvent(ClickEvent.openUrl("https://discord.gg/lunaramc")))
.clickEvent(ClickEvent.openUrl("https://discord.gg/V32MCYsFhn")))
.append(Component.newline())
.build();

View File

@ -5,6 +5,7 @@ import net.luckperms.api.LuckPerms;
import net.luckperms.api.model.user.User;
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.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@ -17,14 +18,14 @@ public class ConnectionListener implements Listener {
this.luckPerms = luckPerms;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onJoin(PlayerJoinEvent event) {
String prefix = getPrefix(event.getPlayer());
String msg = "<dark_gray>[<green>+<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();
event.joinMessage(MiniMessage.miniMessage().deserialize(msg));
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onQuit(PlayerQuitEvent event) {
String prefix = getPrefix(event.getPlayer());
String msg = "<dark_gray>[<red>-<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();

View File

@ -0,0 +1,51 @@
package ch.bensuisse.miningman.features.scoreboard;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Sound;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class ScoreboardCommand implements CommandExecutor, TabCompleter {
private static final TextColor THEME_COLOR = TextColor.fromHexString("#FFA80F");
private final ScoreboardService scoreboardService;
public ScoreboardCommand(ScoreboardService scoreboardService) {
this.scoreboardService = scoreboardService;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("ᴏɴʟʏ ᴘʟᴀʏᴇʀs ᴄᴀɴ ᴜsᴇ ᴛʜɪs ᴄᴏᴍᴍᴀɴᴅ.", NamedTextColor.RED));
return true;
}
boolean enabled = scoreboardService.toggle(player);
String state = enabled ? "ᴇɴᴀʙʟᴇᴅ" : "ᴅɪsᴀʙʟᴇᴅ";
player.sendMessage(Component.text("➤ ", NamedTextColor.WHITE)
.append(Component.text("sᴄᴏʀᴇʙᴏᴀʀᴅ ", THEME_COLOR))
.append(Component.text(state, THEME_COLOR)));
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1.0f, enabled ? 1.2f : 0.8f);
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] args) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,337 @@
package ch.bensuisse.miningman.features.scoreboard;
import ch.bensuisse.miningman.MiningMan;
import ch.bensuisse.miningman.api.ColorUtil;
import io.papermc.paper.scoreboard.numbers.NumberFormat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.model.user.User;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Persönliches Sidebar-Scoreboard: Name, Rang, Tode, Spielzeit, Online-Anzahl, platzierte/abgebaute
* Blöcke, gelaufene Blöcke sowie Server-IP. Standardmäßig für alle Spieler aktiv, kann pro Spieler
* mit /sb ein- oder ausgeblendet werden.
*/
public class ScoreboardService implements Listener {
private static final String OBJECTIVE_NAME = "miningman_sb";
private static final TextColor LABEL_COLOR = TextColor.fromHexString("#FE5A1D");
// Jede Zeile bekommt einen eigenen, satten Farbton - Titel und Icon in derselben Farbe.
private static final TextColor NAME_COLOR = TextColor.fromHexString("#FF5C8A");
private static final TextColor RANK_COLOR = TextColor.fromHexString("#FFD400");
private static final TextColor DEATHS_COLOR = TextColor.fromHexString("#FF3B3B");
private static final TextColor PLAYTIME_COLOR = TextColor.fromHexString("#FFA80F");
private static final TextColor ONLINE_COLOR = TextColor.fromHexString("#39FF6A");
private static final TextColor PLACED_COLOR = TextColor.fromHexString("#B14EFF");
private static final TextColor MINED_COLOR = TextColor.fromHexString("#C68642");
private static final TextColor WALKED_COLOR = TextColor.fromHexString("#22D3EE");
private static final String SEPARATOR = " "; // 25 Zeichen, als Leerzeichen für eine durchgehende Linie
private static final String SERVER_DOMAIN = "ᴍɪɴɪɴɢᴍᴀɴ.eu";
private static final List<Material> MINEABLE_MATERIALS = Arrays.stream(Material.values())
.filter(m -> m.isBlock() && !m.isLegacy())
.toList();
// USE_ITEM verlangt zwingend ein Material, das auch ein Item ist - reine Block-Varianten
// wie ACACIA_WALL_HANGING_SIGN (kein eigenes Item) fliegen sonst mit IllegalArgumentException raus.
private static final List<Material> PLACEABLE_MATERIALS = Arrays.stream(Material.values())
.filter(m -> m.isBlock() && m.isItem() && !m.isLegacy())
.toList();
// Reihenfolge der Zeilen in buildLines(). Nur die dynamischen Indizes werden pro Update
// tatsächlich neu gesendet - Trennlinien, Leerzeilen und die IP-Zeile ändern sich nie.
private static final int LINE_COUNT = 13;
private static final int[] DYNAMIC_LINE_INDICES = { 1, 2, 4, 5, 6, 8, 9, 10 };
// Blöcke platziert/abgebaut erfordern eine Iteration über hunderte Materialien - das lohnt sich
// nicht bei jedem 2-Sekunden-Takt, deshalb wird nur alle paar Zyklen neu gerechnet und zwischengespeichert.
private static final int BLOCK_STATS_REFRESH_INTERVAL = 5; // ~10s bei 2s-Takt
private final MiningMan plugin;
private final LuckPerms luckPerms;
private final List<TextColor> gradientColors;
private final Set<UUID> disabled = new HashSet<>();
private final Map<UUID, PlayerBoard> boards = new HashMap<>();
private final Map<UUID, long[]> blockStatsCache = new HashMap<>();
private int tickCounter = 0;
public ScoreboardService(MiningMan plugin, LuckPerms luckPerms) {
this.plugin = plugin;
this.luckPerms = luckPerms;
this.gradientColors = ColorUtil.createGradient(TextColor.fromHexString("#FE5A1D"),
TextColor.fromHexString("#FFA80F"), 40);
Bukkit.getScheduler().runTaskTimer(plugin, this::updateAll, 20L, 40L);
}
public void onDisable() {
Scoreboard main = Bukkit.getScoreboardManager().getMainScoreboard();
for (Player player : Bukkit.getOnlinePlayers()) {
player.setScoreboard(main);
}
boards.clear();
blockStatsCache.clear();
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Bukkit.getScheduler().runTaskLater(plugin, () -> update(event.getPlayer(), true), 10L);
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
UUID id = event.getPlayer().getUniqueId();
disabled.remove(id);
boards.remove(id);
blockStatsCache.remove(id);
}
/**
* Schaltet das Scoreboard für den Spieler um.
*
* @return true, wenn das Scoreboard danach aktiv ist, sonst false.
*/
public boolean toggle(Player player) {
UUID id = player.getUniqueId();
if (disabled.contains(id)) {
disabled.remove(id);
update(player, true);
return true;
}
disabled.add(id);
boards.remove(id);
player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
return false;
}
public boolean isEnabled(Player player) {
return !disabled.contains(player.getUniqueId());
}
private void updateAll() {
boolean refreshBlockStats = (tickCounter++ % BLOCK_STATS_REFRESH_INTERVAL) == 0;
for (Player player : Bukkit.getOnlinePlayers()) {
if (isEnabled(player))
update(player, refreshBlockStats);
}
}
private void update(Player player, boolean refreshBlockStats) {
if (!isEnabled(player))
return;
boolean isNewBoard = !boards.containsKey(player.getUniqueId());
PlayerBoard board = boards.computeIfAbsent(player.getUniqueId(), id -> createBoard(player));
List<Component> lines = buildLines(player, refreshBlockStats || isNewBoard);
for (int index : DYNAMIC_LINE_INDICES) {
board.teams[index].prefix(lines.get(index));
}
}
/**
* Registriert Objective, Teams und Scores für einen Spieler genau einmal. Danach werden nur
* noch die Team-Prefixe der dynamischen Zeilen aktualisiert, statt das komplette Scoreboard
* jedes Mal neu aufzubauen und an den Client zu senden.
*/
private PlayerBoard createBoard(Player player) {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
Objective objective = scoreboard.registerNewObjective(OBJECTIVE_NAME, Criteria.DUMMY,
ColorUtil.applyGradient("MiningMan", gradientColors).decorate(TextDecoration.BOLD));
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
objective.numberFormat(NumberFormat.blank());
List<Component> lines = buildLines(player, true);
Team[] teams = new Team[LINE_COUNT];
int score = LINE_COUNT;
for (int i = 0; i < LINE_COUNT; i++) {
String entry = uniqueEntry(score);
Team team = scoreboard.registerNewTeam("l" + i);
team.addEntry(entry);
team.prefix(lines.get(i));
objective.getScore(entry).setScore(score);
teams[i] = team;
score--;
}
player.setScoreboard(scoreboard);
return new PlayerBoard(teams);
}
private List<Component> buildLines(Player player, boolean refreshBlockStats) {
long[] blockStats = getBlockStats(player, refreshBlockStats);
List<Component> lines = new ArrayList<>();
lines.add(separator());
lines.add(nameLine(player));
lines.add(rankLine(player));
lines.add(Component.empty());
lines.add(statLine("☠", DEATHS_COLOR, "ᴅᴇᴀᴛʜs",
String.valueOf(player.getStatistic(Statistic.DEATHS))));
lines.add(statLine("⌛", PLAYTIME_COLOR, "ᴘʟᴀʏᴛɪᴍᴇ",
formatPlaytime(player.getStatistic(Statistic.PLAY_ONE_MINUTE))));
lines.add(statLine("◎", ONLINE_COLOR, "ᴏɴʟɪɴᴇ",
Bukkit.getOnlinePlayers().size() + "/" + Bukkit.getMaxPlayers()));
lines.add(Component.empty());
lines.add(statLine("⧉", PLACED_COLOR, "ᴘʟᴀᴄᴇᴅ", formatNumber(blockStats[1])));
lines.add(statLine("⛏", MINED_COLOR, "ᴍɪɴᴇᴅ", formatNumber(blockStats[0])));
lines.add(statLine("⇄", WALKED_COLOR, "ᴡᴀʟᴋᴇᴅ", formatNumber(getBlocksWalked(player))));
lines.add(separator());
lines.add(ipLine());
return lines;
}
private Component separator() {
return ColorUtil.applyGradient(SEPARATOR, gradientColors).decorate(TextDecoration.STRIKETHROUGH);
}
private Component nameLine(Player player) {
return Component.text(" ✎ ", NAME_COLOR)
.append(Component.text("ɴᴀᴍᴇ: ", NAME_COLOR))
.append(Component.text(player.getName(), NamedTextColor.WHITE).decoration(TextDecoration.BOLD, false));
}
private Component rankLine(Player player) {
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
String prefix = user != null ? user.getCachedData().getMetaData().getPrefix() : null;
Component rankValue;
if (prefix != null && !prefix.isEmpty()) {
rankValue = MiniMessage.miniMessage().deserialize(prefix);
} else {
rankValue = Component.text("-", NamedTextColor.WHITE);
}
return Component.text(" ★ ", RANK_COLOR)
.append(Component.text("ʀᴀɴᴋ: ", RANK_COLOR))
.append(rankValue);
}
private Component statLine(String icon, TextColor color, String label, String value) {
return Component.text(" " + icon + " ", color)
.append(Component.text(label + ": ", color))
.append(Component.text(value, NamedTextColor.WHITE));
}
private Component ipLine() {
return Component.text(" → ", LABEL_COLOR)
.append(Component.text(SERVER_DOMAIN, NamedTextColor.WHITE))
.append(Component.text(" ← ", LABEL_COLOR));
}
/**
* Liefert {abgebaut, platziert} für einen Spieler. Wird nur bei refresh=true neu über alle
* Materialien berechnet, sonst kommt der zwischengespeicherte Wert vom letzten Refresh.
*/
private long[] getBlockStats(Player player, boolean refresh) {
UUID id = player.getUniqueId();
if (refresh || !blockStatsCache.containsKey(id)) {
long[] stats = { getBlocksMined(player), getBlocksPlaced(player) };
blockStatsCache.put(id, stats);
return stats;
}
return blockStatsCache.get(id);
}
/**
* Summiert die Bukkit-Statistik MINE_BLOCK über alle Blockarten (abgebaute Blöcke).
*/
private long getBlocksMined(Player player) {
long total = 0;
for (Material material : MINEABLE_MATERIALS) {
total += player.getStatistic(Statistic.MINE_BLOCK, material);
}
return total;
}
/**
* Summiert die Bukkit-Statistik USE_ITEM über alle platzierbaren Blockarten. Bei platzierbaren
* Blöcken zählt "used" vanilla-seitig genau die Platzierungen (siehe Statistik-Screen im Spiel).
*/
private long getBlocksPlaced(Player player) {
long total = 0;
for (Material material : PLACEABLE_MATERIALS) {
total += player.getStatistic(Statistic.USE_ITEM, material);
}
return total;
}
private long getBlocksWalked(Player player) {
long cm = (long) player.getStatistic(Statistic.WALK_ONE_CM) + player.getStatistic(Statistic.SPRINT_ONE_CM);
return cm / 100L;
}
private String formatPlaytime(int ticks) {
long minutes = ticks / 20L / 60L;
long days = minutes / 1440;
long hours = (minutes % 1440) / 60;
long mins = minutes % 60;
if (days > 0)
return days + "d " + hours + "h";
if (hours > 0)
return hours + "h " + mins + "m";
return mins + "m";
}
/**
* Zeigt Zahlen bis 9999 exakt an (z.B. 9640) und kürzt erst ab 10'000 auf "k" bzw.
* ab 1'000'000 auf "m" (z.B. 12k, 100k, 1m).
*/
private String formatNumber(long value) {
if (value < 10_000)
return String.valueOf(value);
if (value < 1_000_000)
return (value / 1000) + "k";
return (value / 1_000_000) + "m";
}
/**
* Erzeugt eine unsichtbare, eindeutige Score-Entry aus Farbcodes (mehrere Zeilen ohne
* sichtbaren Text sind sonst nicht möglich, da jede Zeile im Scoreboard einen eigenen
* eindeutigen Eintrag benötigt).
*/
private String uniqueEntry(int index) {
StringBuilder sb = new StringBuilder();
int n = index + 1;
while (n > 0) {
sb.append(ChatColor.COLOR_CHAR).append(Integer.toHexString(n % 16));
n /= 16;
}
return sb.toString();
}
private record PlayerBoard(Team[] teams) {
}
}

View File

@ -32,8 +32,8 @@ public class TablistService implements Listener {
this.plugin = plugin;
this.luckPerms = luckPerms;
this.gradientColors = ColorUtil.createGradient(TextColor.fromHexString("#1E8967"),
TextColor.fromHexString("#00FCFC"), 18);
this.gradientColors = ColorUtil.createGradient(TextColor.fromHexString("#FE5A1D"),
TextColor.fromHexString("#FFA80F"), 18);
Bukkit.getScheduler().runTaskTimer(plugin, this::updateAll, 20L, 40L);
}
@ -54,6 +54,7 @@ public class TablistService implements Listener {
private void updateAll() {
updateSorting();
updatePlayerListNames();
for (Player player : Bukkit.getOnlinePlayers()) {
updateHeaderFooter(player);
}
@ -138,8 +139,37 @@ public class TablistService implements Listener {
}
}
private void updatePlayerListNames() {
for (Player target : Bukkit.getOnlinePlayers()) {
User user = luckPerms.getUserManager().getUser(target.getUniqueId());
String prefix = user != null ? user.getCachedData().getMetaData().getPrefix() : null;
Component nameComponent = Component.text(target.getName(), NamedTextColor.WHITE)
.decoration(TextDecoration.BOLD, false);
if (prefix != null && !prefix.isEmpty()) {
Component rankComp = MiniMessage.miniMessage().deserialize(prefix);
nameComponent = rankComp.append(Component.text(" ").decoration(TextDecoration.BOLD, false))
.append(nameComponent);
}
target.playerListName(nameComponent);
}
}
private TextColor getPingColor(int ping) {
if (ping <= 50) return NamedTextColor.GREEN;
if (ping <= 150) return NamedTextColor.YELLOW;
return NamedTextColor.RED;
}
private TextColor getTpsColor(double tps) {
if (tps >= 18.0) return NamedTextColor.GREEN;
if (tps >= 15.0) return NamedTextColor.YELLOW;
return NamedTextColor.RED;
}
private void updateHeaderFooter(Player player) {
Component title = ColorUtil.applyGradient("ʟᴜɴᴀʀᴀᴍᴄ.xʏᴢ", gradientColors)
Component title = ColorUtil.applyGradient("ᴍɪɴɪɴɢᴍᴀɴ.eu", gradientColors)
.decorate(TextDecoration.BOLD);
int playerCount = Bukkit.getOnlinePlayers().size();
@ -151,16 +181,19 @@ public class TablistService implements Listener {
.append(players)
.append(Component.newline());
Component storeLine = Component.text("sᴛᴏʀᴇ: ", TextColor.fromHexString("#FC0000"))
.append(Component.text("ᴄᴏᴍᴍɪɴɢ sᴏᴏɴ", NamedTextColor.WHITE));
Component discordLine = Component.text(" ᴅɪsᴄᴏʀᴅ: ", TextColor.fromHexString("#FE5A1D"))
.append(Component.text("discord.gg/V32MCYsFhn ", NamedTextColor.WHITE));
Component discordLine = Component.text(" ᴅɪsᴄᴏʀᴅ: ", TextColor.fromHexString("#5454FC"))
.append(Component.text("ᴅɪsᴄᴏʀᴅ.ɢɢ/ʟᴜɴᴀʀᴀᴍᴄ ", NamedTextColor.WHITE));
double tps = Math.min(20.0, Bukkit.getServer().getTPS()[0]);
Component tpsLine = Component.text(" ᴛᴘs: ", TextColor.fromHexString("#FE5A1D"))
.append(Component.text(String.format("%.1f", tps), getTpsColor(tps)))
.append(Component.text(" ᴘɪɴɢ: ", TextColor.fromHexString("#FE5A1D")))
.append(Component.text(player.getPing() + "ms", getPingColor(player.getPing())));
Component footer = Component.newline()
.append(storeLine)
.append(Component.newline())
.append(discordLine)
.append(Component.newline())
.append(tpsLine)
.append(Component.newline());
player.sendPlayerListHeaderAndFooter(header, footer);

View File

@ -13,6 +13,5 @@ public class JoinManager implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
event.getPlayer().setLevel(2026);
}
}

View File

@ -1,5 +1,5 @@
name: MiningMan
version: '1.0'
version: '2.0'
main: ch.bensuisse.miningman.MiningMan
api-version: '1.21'
authors: [BenSuisse]
@ -18,3 +18,5 @@ commands:
usage: /clearchat
aliases: [cc]
permission: miningman.clearchat
sb:
usage: /sb

View File

@ -1,20 +0,0 @@
name: MiningMan
version: '1.0'
main: ch.bensuisse.miningman.MiningMan
api-version: '1.21'
authors: [BenSuisse]
depend:
- LuckPerms
commands:
announce:
usage: /announce <small|big|restart> <nachricht>
permission: miningman.announce
discord:
usage: /discord
dc:
usage: /dc
clearchat:
usage: /clearchat
aliases: [cc]
permission: miningman.clearchat

View File

@ -1,10 +0,0 @@
ch\bensuisse\miningman\features\announce\AnnounceCommand.class
ch\bensuisse\miningman\MiningMan.class
ch\bensuisse\miningman\commands\SocialCommands.class
ch\bensuisse\miningman\features\tablist\TablistService.class
ch\bensuisse\miningman\api\ColorUtil.class
ch\bensuisse\miningman\managers\JoinManager.class
ch\bensuisse\miningman\features\chat\ClearChatCommand.class
ch\bensuisse\miningman\features\announce\AnnounceCommand$1.class
ch\bensuisse\miningman\features\chat\ChatListener.class
ch\bensuisse\miningman\features\general\ConnectionListener.class

View File

@ -1,9 +0,0 @@
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\api\ColorUtil.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\commands\SocialCommands.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\features\announce\AnnounceCommand.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\features\chat\ChatListener.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\features\chat\ClearChatCommand.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\features\general\ConnectionListener.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\features\tablist\TablistService.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\managers\JoinManager.java
C:\Users\benib\Dev\Intellij\Minecraft\LunaraSystem\src\main\java\ch\bensuisse\miningman\MiningMan.java