Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9f53b1af | ||
|
|
fab579d5af | ||
|
|
dc92ac9c4c | ||
|
|
5d9e0ba822 |
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>ch.bensuisse</groupId>
|
<groupId>ch.bensuisse</groupId>
|
||||||
<artifactId>MiningMan</artifactId>
|
<artifactId>MiningMan</artifactId>
|
||||||
<version>1.2</version>
|
<version>2.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>MiningMan</name>
|
<name>MiningMan</name>
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import ch.bensuisse.miningman.features.announce.AnnounceCommand;
|
|||||||
import ch.bensuisse.miningman.features.chat.ChatListener;
|
import ch.bensuisse.miningman.features.chat.ChatListener;
|
||||||
import ch.bensuisse.miningman.features.chat.ClearChatCommand;
|
import ch.bensuisse.miningman.features.chat.ClearChatCommand;
|
||||||
import ch.bensuisse.miningman.features.general.ConnectionListener;
|
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.features.tablist.TablistService;
|
||||||
import ch.bensuisse.miningman.managers.JoinManager;
|
import ch.bensuisse.miningman.managers.JoinManager;
|
||||||
import net.luckperms.api.LuckPerms;
|
import net.luckperms.api.LuckPerms;
|
||||||
@ -16,6 +18,7 @@ public final class MiningMan extends JavaPlugin {
|
|||||||
|
|
||||||
private LuckPerms luckPerms;
|
private LuckPerms luckPerms;
|
||||||
private TablistService tablistService;
|
private TablistService tablistService;
|
||||||
|
private ScoreboardService scoreboardService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
@ -26,6 +29,7 @@ public final class MiningMan extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tablistService = new TablistService(this, luckPerms);
|
this.tablistService = new TablistService(this, luckPerms);
|
||||||
|
this.scoreboardService = new ScoreboardService(this, luckPerms);
|
||||||
|
|
||||||
registerListeners();
|
registerListeners();
|
||||||
registerCommands();
|
registerCommands();
|
||||||
@ -38,6 +42,8 @@ public final class MiningMan extends JavaPlugin {
|
|||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
if (tablistService != null)
|
if (tablistService != null)
|
||||||
tablistService.onDisable();
|
tablistService.onDisable();
|
||||||
|
if (scoreboardService != null)
|
||||||
|
scoreboardService.onDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean setupLuckPerms() {
|
private boolean setupLuckPerms() {
|
||||||
@ -55,6 +61,7 @@ public final class MiningMan extends JavaPlugin {
|
|||||||
pm.registerEvents(new ConnectionListener(luckPerms), this);
|
pm.registerEvents(new ConnectionListener(luckPerms), this);
|
||||||
pm.registerEvents(new ChatListener(this), this);
|
pm.registerEvents(new ChatListener(this), this);
|
||||||
pm.registerEvents(this.tablistService, this);
|
pm.registerEvents(this.tablistService, this);
|
||||||
|
pm.registerEvents(this.scoreboardService, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCommands() {
|
private void registerCommands() {
|
||||||
@ -71,9 +78,17 @@ public final class MiningMan extends JavaPlugin {
|
|||||||
ClearChatCommand clearChatCommand = new ClearChatCommand();
|
ClearChatCommand clearChatCommand = new ClearChatCommand();
|
||||||
getCommand("clearchat").setExecutor(clearChatCommand);
|
getCommand("clearchat").setExecutor(clearChatCommand);
|
||||||
getCommand("clearchat").setTabCompleter(clearChatCommand);
|
getCommand("clearchat").setTabCompleter(clearChatCommand);
|
||||||
|
|
||||||
|
ScoreboardCommand scoreboardCommand = new ScoreboardCommand(scoreboardService);
|
||||||
|
getCommand("sb").setExecutor(scoreboardCommand);
|
||||||
|
getCommand("sb").setTabCompleter(scoreboardCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TablistService getTablistService() {
|
public TablistService getTablistService() {
|
||||||
return tablistService;
|
return tablistService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScoreboardService getScoreboardService() {
|
||||||
|
return scoreboardService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import net.luckperms.api.LuckPerms;
|
|||||||
import net.luckperms.api.model.user.User;
|
import net.luckperms.api.model.user.User;
|
||||||
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.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
@ -17,14 +18,14 @@ public class ConnectionListener implements Listener {
|
|||||||
this.luckPerms = luckPerms;
|
this.luckPerms = luckPerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
public void onJoin(PlayerJoinEvent event) {
|
public void onJoin(PlayerJoinEvent event) {
|
||||||
String prefix = getPrefix(event.getPlayer());
|
String prefix = getPrefix(event.getPlayer());
|
||||||
String msg = "<dark_gray>[<green>+<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();
|
String msg = "<dark_gray>[<green>+<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();
|
||||||
event.joinMessage(MiniMessage.miniMessage().deserialize(msg));
|
event.joinMessage(MiniMessage.miniMessage().deserialize(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
public void onQuit(PlayerQuitEvent event) {
|
public void onQuit(PlayerQuitEvent event) {
|
||||||
String prefix = getPrefix(event.getPlayer());
|
String prefix = getPrefix(event.getPlayer());
|
||||||
String msg = "<dark_gray>[<red>-<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();
|
String msg = "<dark_gray>[<red>-<dark_gray>] <reset>" + prefix + " <white>" + event.getPlayer().getName();
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -152,11 +152,7 @@ public class TablistService implements Listener {
|
|||||||
.append(nameComponent);
|
.append(nameComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ping = target.getPing();
|
target.playerListName(nameComponent);
|
||||||
Component pingComponent = Component.text(" [" + ping + "ms]", getPingColor(ping))
|
|
||||||
.decoration(TextDecoration.BOLD, false);
|
|
||||||
|
|
||||||
target.playerListName(nameComponent.append(pingComponent));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,5 @@ public class JoinManager implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onJoin(PlayerJoinEvent event) {
|
public void onJoin(PlayerJoinEvent event) {
|
||||||
event.getPlayer().setLevel(2026);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: MiningMan
|
name: MiningMan
|
||||||
version: '1.2'
|
version: '2.0'
|
||||||
main: ch.bensuisse.miningman.MiningMan
|
main: ch.bensuisse.miningman.MiningMan
|
||||||
api-version: '1.21'
|
api-version: '1.21'
|
||||||
authors: [BenSuisse]
|
authors: [BenSuisse]
|
||||||
@ -18,3 +18,5 @@ commands:
|
|||||||
usage: /clearchat
|
usage: /clearchat
|
||||||
aliases: [cc]
|
aliases: [cc]
|
||||||
permission: miningman.clearchat
|
permission: miningman.clearchat
|
||||||
|
sb:
|
||||||
|
usage: /sb
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user