Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd3567d157 | |||
| 98d08b627d | |||
| 114b0e83d7 | |||
| df4ee6c2e9 |
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>me.viper</groupId>
|
||||
<artifactId>Team</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.5</version>
|
||||
<name>TeamPlugin</name>
|
||||
<description>Erweitertes konfigurierbares Team-Plugin (Spigot 1.21.x)</description>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import me.viper.teamplugin.commands.TeamCommand;
|
||||
import me.viper.teamplugin.listener.InventoryListener;
|
||||
import me.viper.teamplugin.manager.DataManager;
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.util.ConfigUpdater;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@@ -16,8 +17,9 @@ public class Main extends JavaPlugin {
|
||||
instance = this;
|
||||
|
||||
saveDefaultConfig(); // config.yml
|
||||
saveResource("lang_de.yml", false); // German lang file (only if absent)
|
||||
saveResource("lang_en.yml", false); // English lang file (only if absent)
|
||||
ConfigUpdater.update(); // fehlende Keys automatisch ergänzen
|
||||
if (!new java.io.File(getDataFolder(), "lang_de.yml").exists()) saveResource("lang_de.yml", false);
|
||||
if (!new java.io.File(getDataFolder(), "lang_en.yml").exists()) saveResource("lang_en.yml", false);
|
||||
|
||||
LangManager.setup();
|
||||
DataManager.setup();
|
||||
|
||||
@@ -7,6 +7,7 @@ import me.viper.teamplugin.gui.MailboxGUI;
|
||||
import me.viper.teamplugin.gui.SettingsGUI;
|
||||
import me.viper.teamplugin.gui.TeamGUI;
|
||||
import me.viper.teamplugin.manager.*;
|
||||
import me.viper.teamplugin.util.ConfigUpdater;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
@@ -54,6 +55,7 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
private static final String C_APPLY = "apply";
|
||||
private static final String C_LOG = "log";
|
||||
private static final String C_MAILBOX = "mailbox";
|
||||
private static final String C_STATUS = "status";
|
||||
|
||||
// apply sub-sub-commands
|
||||
private static final String C_LIST = "list";
|
||||
@@ -74,7 +76,9 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
Map.entry("suchen", C_SEARCH),
|
||||
Map.entry("bewerben", C_APPLY),
|
||||
Map.entry("protokoll", C_LOG),
|
||||
Map.entry("postfach", C_MAILBOX)
|
||||
Map.entry("postfach", C_MAILBOX),
|
||||
Map.entry("status", C_STATUS),
|
||||
Map.entry("offline", C_STATUS)
|
||||
);
|
||||
|
||||
// apply sub-sub DE aliases
|
||||
@@ -120,7 +124,8 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
LangManager.getCmd("cmd_search"),
|
||||
LangManager.getCmd("cmd_apply"),
|
||||
LangManager.getCmd("cmd_log"),
|
||||
LangManager.getCmd("cmd_mailbox")
|
||||
LangManager.getCmd("cmd_mailbox"),
|
||||
LangManager.getCmd("cmd_status")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,6 +244,7 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
case C_RELOAD -> {
|
||||
if (!sender.hasPermission("teamplugin.admin")) { sender.sendMessage(LangManager.get("no_permission")); return true; }
|
||||
Main.getInstance().reloadConfig();
|
||||
ConfigUpdater.update();
|
||||
LangManager.setup();
|
||||
DataManager.reloadData();
|
||||
MailboxManager.reload();
|
||||
@@ -255,7 +261,7 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
String rankDisplay = Main.getInstance().getConfig().getString("rank-settings." + infoRank + ".display", infoRank);
|
||||
String iso = DataManager.getJoinDate(infoName);
|
||||
String joinDate = (iso != null && !iso.isEmpty()) ? Utils.prettifyIso(iso) : "\u00a77\u2014";
|
||||
boolean isOnline = Bukkit.getOfflinePlayer(infoName).isOnline();
|
||||
boolean isOnline = PresenceManager.isShownAsOnline(infoName);
|
||||
String statusOn = Main.getInstance().getConfig().getString("status.online", "&a\uD83D\uDFE2 Online");
|
||||
String statusOff = Main.getInstance().getConfig().getString("status.offline", "&c\uD83D\uDD34 Offline");
|
||||
sender.sendMessage(Utils.color(Utils.replace(LangManager.get("info_header"), "%player%", infoName)));
|
||||
@@ -264,6 +270,32 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
sender.sendMessage(Utils.color(Utils.replace(LangManager.get("info_status"), "%status%", isOnline ? statusOn : statusOff)));
|
||||
}
|
||||
|
||||
// ── status ────────────────────────────────────────────────
|
||||
case C_STATUS -> {
|
||||
if (!(sender instanceof Player p)) { sender.sendMessage(LangManager.get("only_player")); return true; }
|
||||
|
||||
if (findRank(p.getName()) == null) {
|
||||
p.sendMessage(Utils.color(LangManager.get("status_only_team")));
|
||||
return true;
|
||||
}
|
||||
|
||||
String mode = (args.length >= 2) ? args[1].toLowerCase() : "toggle";
|
||||
if (mode.equals("status")) {
|
||||
boolean forced = PresenceManager.isForcedOffline(p.getName());
|
||||
p.sendMessage(Utils.color(forced
|
||||
? LangManager.get("status_state_forced")
|
||||
: LangManager.get("status_state_normal")));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean newForced = !PresenceManager.isForcedOffline(p.getName());
|
||||
|
||||
PresenceManager.setForcedOffline(p.getName(), newForced);
|
||||
p.sendMessage(Utils.color(newForced
|
||||
? LangManager.get("status_enabled")
|
||||
: LangManager.get("status_disabled")));
|
||||
}
|
||||
|
||||
// ── search ────────────────────────────────────────────────
|
||||
case C_SEARCH -> {
|
||||
if (args.length < 2) { sender.sendMessage(Utils.color(LangManager.get("prefix")) + "\u00a7cUsage: /team " + LangManager.getCmd("cmd_search") + " <n>"); return true; }
|
||||
@@ -427,6 +459,7 @@ public class TeamCommand implements CommandExecutor, TabCompleter {
|
||||
case C_SEARCH -> filter(allTeamMembers(), args[1]);
|
||||
case C_LOG -> List.of("10", "25", "50");
|
||||
case C_APPLY -> filter(activeApplySubs(sender.hasPermission("teamplugin.admin")), args[1]);
|
||||
case C_STATUS -> filter(List.of("toggle", "status"), args[1]);
|
||||
default -> new ArrayList<>();
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import me.viper.teamplugin.Main;
|
||||
import me.viper.teamplugin.manager.BackupManager;
|
||||
import me.viper.teamplugin.manager.DataManager;
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.manager.PresenceManager;
|
||||
import me.viper.teamplugin.util.ConfigUpdater;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -59,6 +61,11 @@ public class SettingsGUI {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: show status toggle even if not configured in admin-buttons.
|
||||
if (!hasButtonKey(buttons, "status_toggle")) {
|
||||
inv.setItem(24, createStatusToggleItem(player));
|
||||
}
|
||||
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
@@ -89,6 +96,7 @@ public class SettingsGUI {
|
||||
}
|
||||
case "reload" -> {
|
||||
Main.getInstance().reloadConfig();
|
||||
ConfigUpdater.update();
|
||||
LangManager.setup();
|
||||
DataManager.reloadData();
|
||||
LangManager.save();
|
||||
@@ -113,13 +121,32 @@ public class SettingsGUI {
|
||||
else p.sendMessage(Utils.color(LangManager.get("prefix")) + "§cBackup fehlgeschlagen.");
|
||||
p.closeInventory();
|
||||
}
|
||||
case "status_toggle" -> {
|
||||
boolean next = !PresenceManager.isForcedOffline(p.getName());
|
||||
PresenceManager.setForcedOffline(p.getName(), next);
|
||||
p.sendMessage(Utils.color(next
|
||||
? LangManager.get("status_enabled")
|
||||
: LangManager.get("status_disabled")));
|
||||
openSettings(p);
|
||||
}
|
||||
default -> {
|
||||
p.sendMessage(Utils.color(LangManager.get("prefix")) + "§cUnbekannter Button: " + key);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback click handler for the built-in status toggle item.
|
||||
if (isStatusToggleTitle(title)) {
|
||||
boolean next = !PresenceManager.isForcedOffline(p.getName());
|
||||
PresenceManager.setForcedOffline(p.getName(), next);
|
||||
p.sendMessage(Utils.color(next
|
||||
? LangManager.get("status_enabled")
|
||||
: LangManager.get("status_disabled")));
|
||||
openSettings(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,4 +155,37 @@ public class SettingsGUI {
|
||||
public static String getGuiTitle() {
|
||||
return LangManager.get("settings_gui_title");
|
||||
}
|
||||
|
||||
private static boolean hasButtonKey(List<?> buttons, String key) {
|
||||
if (buttons == null || key == null) return false;
|
||||
for (Object o : buttons) {
|
||||
if (!(o instanceof Map<?, ?> map)) continue;
|
||||
Object raw = map.get("key");
|
||||
if (raw instanceof String s && s.equalsIgnoreCase(key)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ItemStack createStatusToggleItem(Player player) {
|
||||
boolean forced = PresenceManager.isForcedOffline(player.getName());
|
||||
Material mat = forced ? Material.REDSTONE_TORCH : Material.LIME_DYE;
|
||||
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(Utils.color(LangManager.get("settings_status_toggle_title")));
|
||||
meta.setLore(List.of(
|
||||
Utils.color(Utils.replace(LangManager.get("settings_status_toggle_lore_state"), "%state%",
|
||||
forced ? LangManager.get("settings_status_state_offline") : LangManager.get("settings_status_state_online"))),
|
||||
Utils.color(LangManager.get("settings_status_toggle_lore_vanish")),
|
||||
Utils.color(LangManager.get("settings_status_toggle_lore_click"))
|
||||
));
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static boolean isStatusToggleTitle(String title) {
|
||||
return title != null && title.equals(Utils.color(LangManager.get("settings_status_toggle_title")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import me.viper.teamplugin.Main;
|
||||
import me.viper.teamplugin.manager.DataManager;
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.manager.MessageManager;
|
||||
import me.viper.teamplugin.manager.PresenceManager;
|
||||
import me.viper.teamplugin.util.SkinResolver;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -82,6 +83,28 @@ public class TeamGUI {
|
||||
if (slot < 0 || slot >= GUI_SIZE) continue;
|
||||
inv.setItem(slot, createRankOverviewBlock(rank));
|
||||
}
|
||||
|
||||
// Info-Item
|
||||
if (cfg.getBoolean("gui.info-item.enabled", false)) {
|
||||
int infoSlot = cfg.getInt("gui.info-item.slot", 49);
|
||||
if (infoSlot >= 0 && infoSlot < GUI_SIZE) {
|
||||
String skullTex = cfg.getString("gui.info-item.skull_texture", "");
|
||||
ItemStack infoItem = (skullTex != null && !skullTex.isEmpty())
|
||||
? buildCustomSkull(skullTex)
|
||||
: new ItemStack(parseMaterial(cfg.getString("gui.info-item.material", "BOOK"), Material.BOOK));
|
||||
String infoDisplay = cfg.getString("gui.info-item.display", "&eInfo");
|
||||
List<String> infoLore = cfg.getStringList("gui.info-item.lore")
|
||||
.stream().map(Utils::color).collect(Collectors.toList());
|
||||
ItemMeta infoMeta = infoItem.getItemMeta();
|
||||
if (infoMeta != null) {
|
||||
infoMeta.setDisplayName(Utils.color(infoDisplay));
|
||||
infoMeta.setLore(infoLore);
|
||||
infoItem.setItemMeta(infoMeta);
|
||||
}
|
||||
inv.setItem(infoSlot, infoItem);
|
||||
}
|
||||
}
|
||||
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
@@ -267,6 +290,20 @@ public class TeamGUI {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Info-Item click
|
||||
if (cfg.getBoolean("gui.info-item.enabled", false)
|
||||
&& slot == cfg.getInt("gui.info-item.slot", 49)) {
|
||||
String action = cfg.getString("gui.info-item.on-click", "");
|
||||
if (action != null && !action.isEmpty()) {
|
||||
player.closeInventory();
|
||||
if (action.startsWith("cmd:")) {
|
||||
player.performCommand(action.substring(4));
|
||||
} else {
|
||||
player.sendMessage(Utils.color(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleRankPageClick(Player player, int slot, String currentRank) {
|
||||
@@ -389,8 +426,7 @@ public class TeamGUI {
|
||||
|
||||
String statusOnline = cfg.getString("status.online", "&a\uD83D\uDFE2 Online");
|
||||
String statusOffline = cfg.getString("status.offline", "&c\uD83D\uDD34 Offline");
|
||||
@SuppressWarnings("deprecation")
|
||||
boolean online = Bukkit.getOfflinePlayer(name).isOnline();
|
||||
boolean online = PresenceManager.isShownAsOnline(name);
|
||||
lore.add(Utils.color("&7Status&8: " + (online ? statusOnline : statusOffline)));
|
||||
|
||||
String iso = DataManager.getData().getString("JoinDates." + name, "");
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DataManager {
|
||||
private static final String FORCE_OFFLINE_PATH = "Status.force-offline";
|
||||
|
||||
private static File file;
|
||||
private static FileConfiguration data;
|
||||
|
||||
@@ -94,6 +96,7 @@ public class DataManager {
|
||||
}
|
||||
if (removed) {
|
||||
data.set("JoinDates." + name, null);
|
||||
data.set(FORCE_OFFLINE_PATH + "." + normalizeName(name), null);
|
||||
save();
|
||||
}
|
||||
}
|
||||
@@ -177,4 +180,17 @@ public class DataManager {
|
||||
public static String getJoinDate(String name) {
|
||||
return data.getString("JoinDates." + name, "");
|
||||
}
|
||||
|
||||
public static boolean isForcedOffline(String name) {
|
||||
return data.getBoolean(FORCE_OFFLINE_PATH + "." + normalizeName(name), false);
|
||||
}
|
||||
|
||||
public static void setForcedOffline(String name, boolean forcedOffline) {
|
||||
data.set(FORCE_OFFLINE_PATH + "." + normalizeName(name), forcedOffline ? true : null);
|
||||
save();
|
||||
}
|
||||
|
||||
private static String normalizeName(String name) {
|
||||
return name == null ? "" : name.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LangManager {
|
||||
@@ -17,6 +21,8 @@ public class LangManager {
|
||||
// ── Setup ─────────────────────────────────────────────────────────
|
||||
|
||||
public static void setup() {
|
||||
syncLanguageFiles();
|
||||
|
||||
String lang = Main.getInstance().getConfig().getString("language", "de").toLowerCase();
|
||||
String fileName = "lang_" + lang + ".yml";
|
||||
|
||||
@@ -31,6 +37,58 @@ public class LangManager {
|
||||
"[LangManager] Loaded language: " + lang + " (" + fileName + ")");
|
||||
}
|
||||
|
||||
private static void syncLanguageFiles() {
|
||||
syncLanguageFile("lang_de.yml");
|
||||
syncLanguageFile("lang_en.yml");
|
||||
}
|
||||
|
||||
private static void syncLanguageFile(String fileName) {
|
||||
Main plugin = Main.getInstance();
|
||||
File target = new File(plugin.getDataFolder(), fileName);
|
||||
|
||||
if (!target.exists()) {
|
||||
plugin.saveResource(fileName, false);
|
||||
plugin.getLogger().info("[LangManager] Created missing file: " + fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration onDisk = YamlConfiguration.loadConfiguration(target);
|
||||
YamlConfiguration defaults = loadLangDefaults(plugin, fileName);
|
||||
if (defaults == null) return;
|
||||
|
||||
List<String> added = new ArrayList<>();
|
||||
for (String path : defaults.getKeys(true)) {
|
||||
if (defaults.isConfigurationSection(path)) continue;
|
||||
if (!onDisk.isSet(path)) {
|
||||
onDisk.set(path, defaults.get(path));
|
||||
added.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (added.isEmpty()) return;
|
||||
|
||||
try {
|
||||
onDisk.save(target);
|
||||
plugin.getLogger().info("[LangManager] " + fileName + ": " + added.size()
|
||||
+ " missing key(s) added.");
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[LangManager] Failed to save " + fileName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static YamlConfiguration loadLangDefaults(Main plugin, String fileName) {
|
||||
try (InputStream in = plugin.getResource(fileName)) {
|
||||
if (in == null) {
|
||||
plugin.getLogger().warning("[LangManager] Missing bundled language file: " + fileName);
|
||||
return null;
|
||||
}
|
||||
return YamlConfiguration.loadConfiguration(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[LangManager] Failed to load bundled " + fileName + ": " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Getters ───────────────────────────────────────────────────────
|
||||
|
||||
public static String get(String path) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.viper.teamplugin.manager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Zentraler Präsenz-Status für Team-Anzeige.
|
||||
* Spieler gelten als offline, wenn sie wirklich offline sind,
|
||||
* manuell auf offline gesetzt wurden oder im Vanish sind.
|
||||
*/
|
||||
public final class PresenceManager {
|
||||
|
||||
private PresenceManager() {}
|
||||
|
||||
public static boolean isShownAsOnline(String playerName) {
|
||||
if (playerName == null || playerName.isEmpty()) return false;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
OfflinePlayer off = Bukkit.getOfflinePlayer(playerName);
|
||||
if (!off.isOnline()) return false;
|
||||
|
||||
Player player = off.getPlayer();
|
||||
if (player == null) return false;
|
||||
|
||||
if (DataManager.isForcedOffline(playerName)) return false;
|
||||
return !isVanished(player);
|
||||
}
|
||||
|
||||
public static boolean isForcedOffline(String playerName) {
|
||||
return DataManager.isForcedOffline(playerName);
|
||||
}
|
||||
|
||||
public static void setForcedOffline(String playerName, boolean forcedOffline) {
|
||||
DataManager.setForcedOffline(playerName, forcedOffline);
|
||||
}
|
||||
|
||||
private static boolean isVanished(Player player) {
|
||||
if (player == null) return false;
|
||||
|
||||
// Common metadata keys used by vanish plugins.
|
||||
// Important: check metadata BOOLEAN value, not just key existence.
|
||||
if (hasTrueMetadata(player, "vanished")
|
||||
|| hasTrueMetadata(player, "vanish")
|
||||
|| hasTrueMetadata(player, "essentials.vanish")
|
||||
|| hasTrueMetadata(player, "supervanish")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback for plugins that toggle entity invisibility directly.
|
||||
return player.isInvisible();
|
||||
}
|
||||
|
||||
private static boolean hasTrueMetadata(Player player, String key) {
|
||||
List<MetadataValue> values = player.getMetadata(key);
|
||||
for (MetadataValue value : values) {
|
||||
if (value != null && value.asBoolean()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
135
src/main/java/me/viper/teamplugin/util/ConfigUpdater.java
Normal file
135
src/main/java/me/viper/teamplugin/util/ConfigUpdater.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package me.viper.teamplugin.util;
|
||||
|
||||
import me.viper.teamplugin.Main;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Versionierte config.yml-Migration:
|
||||
* – Es werden niemals pauschal alle fehlenden Keys ergänzt.
|
||||
* – Ergänzt werden nur explizit definierte, neue Migrations-Keys pro Version.
|
||||
* – User-verwaltete Rangbereiche werden nie automatisch zurückgeschrieben.
|
||||
*/
|
||||
public class ConfigUpdater {
|
||||
|
||||
private static final String CONFIG_VERSION_PATH = "config-version";
|
||||
private static final Set<String> MIGRATION_EXCLUDED_ROOTS = Set.of(
|
||||
"ranks",
|
||||
"rank-settings"
|
||||
);
|
||||
|
||||
/**
|
||||
* Nur hier eingetragene Pfade werden bei einem Upgrade auf die jeweilige Version ergänzt.
|
||||
* Format: targetVersion -> List<configPath>
|
||||
*
|
||||
* Wichtig: Nur wirklich neue Keys eintragen, niemals bestehende User-Bereiche.
|
||||
*/
|
||||
private static final Map<Integer, List<String>> MIGRATION_ADDITIONS = Map.of(
|
||||
1, List.of()
|
||||
);
|
||||
|
||||
public static void update() {
|
||||
Main plugin = Main.getInstance();
|
||||
File configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
|
||||
YamlConfiguration defaults = loadDefaults(plugin);
|
||||
if (defaults == null) return;
|
||||
|
||||
YamlConfiguration onDisk = YamlConfiguration.loadConfiguration(configFile);
|
||||
int defaultVersion = Math.max(1, defaults.getInt(CONFIG_VERSION_PATH, 1));
|
||||
int diskVersion = onDisk.getInt(CONFIG_VERSION_PATH, 0);
|
||||
|
||||
if (diskVersion > defaultVersion) {
|
||||
plugin.getLogger().warning("[Config] config.yml hat eine neuere Version (" + diskVersion
|
||||
+ ") als dieses Plugin (" + defaultVersion + ").");
|
||||
return;
|
||||
}
|
||||
|
||||
if (diskVersion >= defaultVersion) {
|
||||
// Bereits aktuell: keine Migration ausführen.
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> added = new ArrayList<>();
|
||||
|
||||
// Migrationen inkrementell je Versionsschritt anwenden.
|
||||
for (int targetVersion = diskVersion + 1; targetVersion <= defaultVersion; targetVersion++) {
|
||||
List<String> additions = MIGRATION_ADDITIONS.getOrDefault(targetVersion, List.of());
|
||||
for (String path : additions) {
|
||||
addPathIfMissing(plugin, onDisk, defaults, path, added);
|
||||
}
|
||||
}
|
||||
|
||||
// Nach erfolgreicher Migration Version anheben.
|
||||
onDisk.set(CONFIG_VERSION_PATH, defaultVersion);
|
||||
|
||||
try {
|
||||
onDisk.save(configFile);
|
||||
plugin.getLogger().info("[Config] Migration " + diskVersion + " -> " + defaultVersion
|
||||
+ " abgeschlossen; " + added.size() + " fehlende Einstellung(en) ergänzt.");
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[Config] Fehler beim Speichern der config.yml: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Neu gespeicherte Migration sofort in die Runtime laden.
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
|
||||
private static YamlConfiguration loadDefaults(Main plugin) {
|
||||
try (InputStream ds = plugin.getResource("config.yml")) {
|
||||
if (ds == null) return null;
|
||||
return YamlConfiguration.loadConfiguration(new InputStreamReader(ds));
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[Config] Fehler beim Laden der Default-config.yml: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPathIfMissing(Main plugin,
|
||||
YamlConfiguration onDisk,
|
||||
YamlConfiguration defaults,
|
||||
String path,
|
||||
List<String> added) {
|
||||
if (path == null || path.trim().isEmpty()) return;
|
||||
if (shouldSkipAutoMerge(path)) return;
|
||||
|
||||
if (!defaults.isSet(path)) {
|
||||
plugin.getLogger().warning("[Config] Migration-Key nicht in default config vorhanden: " + path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaults.isConfigurationSection(path)) {
|
||||
for (String relPath : defaults.getConfigurationSection(path).getKeys(true)) {
|
||||
String fullPath = path + "." + relPath;
|
||||
if (defaults.isConfigurationSection(fullPath)) continue;
|
||||
if (shouldSkipAutoMerge(fullPath)) continue;
|
||||
if (!onDisk.isSet(fullPath)) {
|
||||
onDisk.set(fullPath, defaults.get(fullPath));
|
||||
added.add(fullPath);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!onDisk.isSet(path)) {
|
||||
onDisk.set(path, defaults.get(path));
|
||||
added.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User-managed rank sections must not be recreated automatically.
|
||||
* Otherwise intentionally removed ranks reappear after /team reload.
|
||||
*/
|
||||
private static boolean shouldSkipAutoMerge(String path) {
|
||||
int dot = path.indexOf('.');
|
||||
String root = dot < 0 ? path : path.substring(0, dot);
|
||||
return MIGRATION_EXCLUDED_ROOTS.contains(root);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,11 @@
|
||||
# GUI-Größe ist IMMER 54 und NICHT konfigurierbar.
|
||||
# ══════════════════════════════════════════════════════
|
||||
|
||||
# Konfigurationsversion für automatische Migrationen
|
||||
# Hinweis: Nur explizit in ConfigUpdater definierte neue Keys werden ergänzt.
|
||||
# Es gibt kein pauschales Wiederherstellen fehlender Einträge.
|
||||
config-version: 1
|
||||
|
||||
# ── Sprache / Language ───────────────────────────────────────────────
|
||||
# de → Deutsche Befehle & Nachrichten (/team hinzufügen, entfernen …)
|
||||
# en → English commands & messages (/team add, del …)
|
||||
@@ -29,6 +34,38 @@ gui:
|
||||
rank_page_title: "&8» &bTeam &7| "
|
||||
background: "GRAY_STAINED_GLASS_PANE"
|
||||
|
||||
# ── Info-Item im Übersichts-GUI ─────────────────────────────────
|
||||
# Zeigt dem Spieler, wie er sich bewerben kann.
|
||||
# slot → Slot-Nummer im Übersichts-GUI (0-53)
|
||||
# material → Beliebiges Vanilla-Material (z. B. BOOK, PAPER, MAP)
|
||||
# display → Anzeigename (& Farbcodes erlaubt)
|
||||
# lore → Zeilenliste (& Farbcodes erlaubt)
|
||||
#
|
||||
# on-click → Aktion beim Klicken (optional):
|
||||
# Leer lassen / weglassen → kein Klick-Effekt
|
||||
# Präfix „cmd:" → Befehl ausführen (als Spieler, ohne /)
|
||||
# Beispiel: cmd:team bewerben
|
||||
# Kein Präfix → Chat-Nachricht senden
|
||||
# Beispiel: "&eBenutze /team bewerben um dich zu bewerben!"
|
||||
#
|
||||
# skull_texture → Skull statt material verwenden (optional)
|
||||
# Gleiche Formate wie bei rank-settings (URL / 64-Hex / Base64 eyJ...)
|
||||
# Wenn gesetzt, wird material ignoriert.
|
||||
info-item:
|
||||
enabled: true
|
||||
slot: 49
|
||||
material: BOOK
|
||||
skull_texture: "http://textures.minecraft.net/texture/d01afe973c5482fdc71e6aa10698833c79c437f21308ea9a1a095746ec274a0f"
|
||||
display: "&e&lBewirb dich!"
|
||||
lore:
|
||||
- "&7Möchtest du Teil unseres Teams werden?"
|
||||
- ""
|
||||
- "&eBefehl&8: &f/team bewerben"
|
||||
- ""
|
||||
- "&7Fülle die Bewerbung aus und schicke sie ab."
|
||||
- "&7Wir melden uns so schnell wie möglich!"
|
||||
on-click: "cmd:team bewerben"
|
||||
|
||||
# ── Rang-Einstellungen ──────────────────────────────────────────────
|
||||
#
|
||||
# Jeder Rang kann folgende Felder haben:
|
||||
@@ -189,4 +226,13 @@ admin-buttons:
|
||||
title: "&6Ränge bearbeiten"
|
||||
lore:
|
||||
- "&7Bearbeite ranks & rank-settings"
|
||||
- "&7in der config.yml"
|
||||
- "&7in der config.yml"
|
||||
|
||||
- key: status_toggle
|
||||
slot: 24
|
||||
material: LIME_DYE
|
||||
title: "&bStatus umschalten"
|
||||
lore:
|
||||
- "&7Schaltet deinen Team-Status"
|
||||
- "&7zwischen online/offline um"
|
||||
- "&8(Vanish wird automatisch als offline erkannt)"
|
||||
@@ -18,6 +18,7 @@ cmd_apply_accept: "annehmen"
|
||||
cmd_apply_deny: "ablehnen"
|
||||
cmd_log: "protokoll"
|
||||
cmd_mailbox: "postfach"
|
||||
cmd_status: "status"
|
||||
|
||||
# ── Allgemein ────────────────────────────────────────────────────────
|
||||
no_permission: "%prefix%§cDazu hast du keine Berechtigung!"
|
||||
@@ -46,6 +47,13 @@ info_joined: "%prefix%§7Beigetreten§8: §e%joindate%"
|
||||
info_status: "%prefix%§7Status§8: %status%"
|
||||
info_not_team: "%prefix%§c%player% ist kein Teammitglied."
|
||||
|
||||
# ── /team status ────────────────────────────────────────────────────
|
||||
status_only_team: "%prefix%§cNur Teammitglieder können diesen Status setzen."
|
||||
status_state_forced: "%prefix%§7Du wirst aktuell als §coffline §7angezeigt."
|
||||
status_state_normal: "%prefix%§7Du wirst aktuell als §aonline §7angezeigt (sofern nicht vanished)."
|
||||
status_enabled: "%prefix%§aOffline-Modus aktiviert. Du wirst in Team-Infos als offline angezeigt."
|
||||
status_disabled: "%prefix%§aOffline-Modus deaktiviert. Du wirst wieder normal angezeigt."
|
||||
|
||||
# ── /team bewerben ───────────────────────────────────────────────────
|
||||
apply_sent: "%prefix%§aDeine Bewerbung für §e%rank% §awurde eingereicht!"
|
||||
apply_already: "%prefix%§cDu hast bereits eine offene Bewerbung."
|
||||
@@ -71,6 +79,12 @@ settings_reload_lore:
|
||||
settings_backup: "§eBackups verwalten"
|
||||
settings_backup_lore:
|
||||
- "§7Öffnet die Backup-Übersicht"
|
||||
settings_status_toggle_title: "&bStatus umschalten"
|
||||
settings_status_toggle_lore_state: "&7Aktuell: %state%"
|
||||
settings_status_toggle_lore_vanish: "&8(Bei Vanish immer offline)"
|
||||
settings_status_toggle_lore_click: "&eKlicken zum Umschalten"
|
||||
settings_status_state_online: "&aOnline"
|
||||
settings_status_state_offline: "&cOffline"
|
||||
|
||||
# ── Rang-Seite: Navigation ────────────────────────────────────────────
|
||||
nav_prev_label: "§7« %rank%"
|
||||
|
||||
@@ -17,6 +17,7 @@ cmd_apply_accept: "accept"
|
||||
cmd_apply_deny: "deny"
|
||||
cmd_log: "log"
|
||||
cmd_mailbox: "mailbox"
|
||||
cmd_status: "status"
|
||||
|
||||
# ── General ──────────────────────────────────────────────────────────
|
||||
no_permission: "%prefix%§cYou don't have permission to do that!"
|
||||
@@ -45,6 +46,13 @@ info_joined: "%prefix%§7Joined§8: §e%joindate%"
|
||||
info_status: "%prefix%§7Status§8: %status%"
|
||||
info_not_team: "%prefix%§c%player% is not a team member."
|
||||
|
||||
# ── /team status ────────────────────────────────────────────────────
|
||||
status_only_team: "%prefix%§cOnly team members can change this status."
|
||||
status_state_forced: "%prefix%§7You are currently shown as §coffline§7."
|
||||
status_state_normal: "%prefix%§7You are currently shown as §aonline§7 (unless vanished)."
|
||||
status_enabled: "%prefix%§aOffline mode enabled. You will be shown as offline in team info."
|
||||
status_disabled: "%prefix%§aOffline mode disabled. You will be shown normally again."
|
||||
|
||||
# ── /team apply ───────────────────────────────────────────────────────
|
||||
apply_sent: "%prefix%§aYour application for §e%rank% §ahas been submitted!"
|
||||
apply_already: "%prefix%§cYou already have a pending application."
|
||||
@@ -70,6 +78,12 @@ settings_reload_lore:
|
||||
settings_backup: "§eManage backups"
|
||||
settings_backup_lore:
|
||||
- "§7Opens the backup overview"
|
||||
settings_status_toggle_title: "&bToggle status"
|
||||
settings_status_toggle_lore_state: "&7Current: %state%"
|
||||
settings_status_toggle_lore_vanish: "&8(Always offline while vanished)"
|
||||
settings_status_toggle_lore_click: "&eClick to toggle"
|
||||
settings_status_state_online: "&aOnline"
|
||||
settings_status_state_offline: "&cOffline"
|
||||
|
||||
# ── Rank page navigation ──────────────────────────────────────────────
|
||||
nav_prev_label: "§7« %rank%"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name: Team
|
||||
version: 1.0.2
|
||||
version: 1.0.5
|
||||
main: me.viper.teamplugin.Main
|
||||
api-version: 1.21
|
||||
author: Viper
|
||||
author: M_Viper
|
||||
description: Erweiterbares Team-Plugin mit GUI, Backup/Restore und vielen Config-Optionen
|
||||
|
||||
commands:
|
||||
|
||||
Reference in New Issue
Block a user