13 Commits
1.0.1 ... main

18 changed files with 1038 additions and 686 deletions

186
README.md
View File

@@ -1,132 +1,118 @@
# 🎄 Minecraft Adventskalender Plugin <div align="center">
Ein hochgradig konfigurierbares Adventskalender-Plugin für **Spigot/Paper-Server**, das dir die volle Kontrolle über Belohnungen, Sprache und Funktionen gibt. # ❄️ Minecraft Adventskalender ❄️
Perfekt, um die Weihnachtszeit auf deinem Server zu versüßen!
*Bringe festliche Stimmung auf deinen Server mit einem hochgradig anpassbaren System*
![Version](https://img.shields.io/badge/Version-1.18.x--1.21.x-green.svg)
![Java](https://img.shields.io/badge/Java-17+-orange.svg)
![Update Checker](https://img.shields.io/badge/Update--Checker-Klickbar-blue.svg)
![Type](https://img.shields.io/badge/Type-Event-red.svg)
</div>
--- ---
## 🖥️ Adventskalender GUI ## Über Minecraft Adventskalender
![Adventskalender GUI](https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/03.png) Das **Minecraft Adventskalender Plugin** ist die ultimative Lösung für deinen Server im Dezember! Es bietet volle Kontrolle über Belohnungen, Sprache und Funktionen. Dank des neuen klickbaren Update-Checkers und dem erweiterten Admin-Test-System war die Verwaltung eines Events noch nie so einfach. Perfekt für Community-Events, um die Spielerbindung in der Weihnachtszeit zu stärken!
![Adventskalender GUI](https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/04.png)
--- ---
## ✨ Features ## ✨ Exklusive Features
- **Anpassbare Belohnungen** - **Vollständig Anpassbare Belohnungen:** Definiere für jeden Tag individuelle Items mit Menge, Name, Lore und Verzauberungen.
Definiere in der `config.yml` für jeden Tag ein eigenes Item, inklusive: - **Smart Update-Checker:** Erhalte beim Joinen als Admin eine dezente, **klickbare Benachrichtigung**, falls eine neue Version verfügbar ist (erscheint verzögert nach 2 Sek.).
- Menge - **Simulations Modus (Admin-Test):** Teste alle Türchen vorab durch Datums-Simulation, ohne deine Systemzeit zu ändern. Inklusive einfachem Reset auf das Echtzeit-Datum.
- Name - **Präzises Reset-System:** Setze einzelne Tage oder komplette Fortschritte zurück wahlweise für einzelne Spieler oder global für den ganzen Server.
- Lore - **Immersive Effekte:** Festliche Sound- und Partikeleffekte beim Öffnen eines Türchens.
- Verzauberungen - **Optimierte Tab-Completion:** Intelligente Befehlsvorschläge mit korrekter numerischer Sortierung (1, 2, 3... statt 1, 10, 11...).
- **Globaler oder individueller Kalender**
Entscheide, ob:
- jeder Spieler seinen eigenen Fortschritt hat oder
- ein Türchen serverweit nur einmal geöffnet werden kann
- **Sound & Partikel**
Angenehme Sound- und Partikeleffekte beim Öffnen eines Türchens.
- **Mehrsprachig**
Unterstützt **Deutsch** und **Englisch**.
Weitere Sprachen können einfach hinzugefügt werden.
- **Admin-Befehle**
Konfigurationen neu laden oder Türchen gezielt für Spieler öffnen.
- **Permissions-System**
Feingranulare Kontrolle über Spieler- und Admin-Rechte.
- **PlaceholderAPI-Unterstützung**
Zeige Statistiken:
- auf Schildern
- in der Tab-Liste
- mit Hologramm-Plugins
- Scoreboard
und vieles mehr.
- **Datenpersistenz**
Spielerfortschritt bleibt auch nach Server-Neustarts erhalten.
--- ---
## 📸 Screenshots ## 🎄 Adventskalender GUI
<div align="center">
<img src="https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/03.png" width="400" alt="Kalender GUI 1">
<img src="https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/04.png" width="400" alt="Kalender GUI 2">
*Die intuitive Kalender-Oberfläche mit allen 24 Türchen*
</div>
---
## ⚙️ Befehle & Permissions
### ▶ Spieler-Befehle
**`/adventskalender`** (Aliase: `/ak`, `/advent`, `/kalender`)
- **Beschreibung:** Öffnet die Kalender-Oberfläche.
- **Berechtigung:** `adventskalender.use`
### ▶ Admin-Befehle
**`/ak admin test <1-24 | reset>`**
- **Beschreibung:** Simuliert einen bestimmten Tag für Belohnungstests. Nutze `reset`, um zum echten Datum zurückzukehren.
- **Berechtigung:** `adventskalender.admin`
**`/ak admin resetday <global | Spieler> <1-24>`**
- **Beschreibung:** Setzt den Status eines spezifischen Tages für einen Spieler oder den gesamten Server zurück.
- **Berechtigung:** `adventskalender.admin`
**`/ak admin reset <global | Spieler>`**
- **Beschreibung:** Löscht den kompletten Fortschritt (alle Tage) für einen Spieler oder global.
- **Berechtigung:** `adventskalender.admin`
**`/ak admin reload`**
- **Beschreibung:** Lädt Config, Sprachdateien und Datenbank-Verbindung im laufenden Betrieb neu.
- **Berechtigung:** `adventskalender.admin`
---
## 📊 PlaceholderAPI (PAPI)
- **`%ak_c%`** - Anzahl bereits geöffneter Türchen.
- **`%ak_n%`** - Nummer des nächsten verfügbaren Türchens.
- **`%ak_d%`** - Verbleibende Tage bis zum 25. Dezember.
### Placeholder auf einem Schild ### Placeholder auf einem Schild
Ein Schild, das die Placeholder des Plugins anzeigt. <div align="center">
<img src="https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/02.png" width="450" alt="Adventskalender Placeholder"> <img src="https://git.viper.ipv64.net/M_Viper/Advenskalender/raw/branch/main/img/02.png" alt="Placeholder Beispiel">
*Ein Schild, das die Placeholder des Plugins anzeigt.*
</div>
--- ---
## 🚀 Installation ## 💬 Support & Community
1. Lade die neueste Version des Plugins als **`.jar`-Datei** herunter. Du hast Fragen oder Feedback? Tritt unserem Discord bei!
2. Platziere die Datei im Ordner:
/plugins <div align="center">
3. Stelle sicher, dass **PlaceholderAPI** installiert ist (Pflichtabhängigkeit). [![Discord Support](https://img.shields.io/badge/Discord-Support-7289DA?style=for-the-badge&logo=discord)](https://discord.com/invite/FdRs4BRd8D)
4. Starte oder starte den Server neu.
5. Passe die Konfigurationsdateien an: /plugins/Adventskalender/ *Klicke auf den Button für direkten Support durch die Community!*
</div>
--- ---
## ⚙️ Konfiguration ## 🔧 Technische Details
Das Plugin verwendet drei Hauptdateien: - **Kompatibilität:** Paper, Spigot, Purpur (1.18.x - 1.21.x)
- **Smart Caching:** Minimale Serverlast durch asynchrone Datenbankaufrufe.
- **`config.yml`** - **Join-Delay:** Update-Meldungen erscheinen erst nach 2 Sekunden, um den Chat-Spam beim Joinen zu umgehen.
- Sprache
- Kalender-Typ (global / individuell)
- Belohnungen für alle 24 Tage
- **`messages_de.yml` / `messages_en.yml`**
- GUI-Titel
- Chat-Nachrichten
- Systemmeldungen
Diese Dateien können frei angepasst oder übersetzt werden.
--- ---
## 📋 Befehle & Berechtigungen <div align="center">
### Befehle *Viper Plugins © 2026 - Effizienz und Magie für deinen Server*
| Befehl | Alias | Beschreibung | Berechtigung | </div>
|------|------|-------------|--------------|
| `/adventskalender` | `/advent`, `/kalender`, `/ak` | Öffnet den Adventskalender | `adventskalender.use` |
| `/adventskalender admin reload` | `/ak admin reload` | Lädt Konfigurations- & Sprachdateien neu | `adventskalender.admin` |
| `/adventskalender admin open <spieler> <tag>` | `/ak admin open ...` | Öffnet ein Türchen für einen Spieler | `adventskalender.admin` |
### Berechtigungen
| Berechtigung | Standard | Beschreibung |
|-------------|----------|--------------|
| `adventskalender.use` | `true` | Erlaubt das Öffnen des Kalenders |
| `adventskalender.admin` | `op` | Erlaubt alle Admin-Befehle |
---
## 🔗 Placeholders
Alle Placeholder sind **PlaceholderAPI-kompatibel** und kurz gehalten.
| Placeholder | Beschreibung |
|------------|--------------|
| `%ak_c%` | Anzahl geöffneter Türchen |
| `%ak_n%` | Nächstes verfügbares Türchen |
| `%ak_d%` | Verbleibende Tage bis 25. Dezember |
### Beispiel für ein Schild
Dein Fortschritt:
%ak_c%/24 Türchen
---

View File

@@ -6,7 +6,7 @@
<groupId>de.mviper</groupId> <groupId>de.mviper</groupId>
<artifactId>adventskalender</artifactId> <artifactId>adventskalender</artifactId>
<version>1.0.0</version> <version>1.0.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>

View File

@@ -1,56 +0,0 @@
package de.mviper.adventskalender;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.Material;
public class AdminCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
Adventskalender.getInstance().reloadConfig();
LanguageManager.setup();
sender.sendMessage(LanguageManager.getString("messages.admin.reload_success"));
return true;
}
if (args.length == 3 && args[0].equalsIgnoreCase("open")) {
Player target = Bukkit.getPlayer(args[1]);
if (target == null) {
sender.sendMessage(LanguageManager.getString("messages.admin.player_not_found").replace("%player%", args[1]));
return true;
}
try {
int day = Integer.parseInt(args[2]);
if (day < 1 || day > 24) {
sender.sendMessage(LanguageManager.getString("messages.admin.invalid_day"));
return true;
}
if (target.getInventory().firstEmpty() == -1) {
target.getWorld().dropItemNaturally(target.getLocation(), AdventInventory.getRewardItem(day));
} else {
target.getInventory().addItem(AdventInventory.getRewardItem(day));
}
CalendarData.setClaimed(target, day);
sender.sendMessage(LanguageManager.getString("messages.admin.open_success").replace("%player%", target.getName()).replace("%day%", String.valueOf(day)));
target.sendMessage(LanguageManager.getString("messages.reward_received").replace("%day%", String.valueOf(day)));
} catch (NumberFormatException e) {
sender.sendMessage(LanguageManager.getString("messages.admin.invalid_day"));
}
return true;
}
sender.sendMessage("§cUsage: /adventskalender admin reload");
sender.sendMessage("§cUsage: /adventskalender admin open <player> <day>");
return true;
}
}

View File

@@ -1,62 +1,69 @@
package de.mviper.adventskalender; package de.mviper.adventskalender;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.time.LocalDate; import org.jetbrains.annotations.NotNull;
import java.time.Month;
import java.time.LocalDate;
public class AdventCalendarExpansion extends PlaceholderExpansion { import java.time.Month;
@Override public class AdventCalendarExpansion extends PlaceholderExpansion {
public String getIdentifier() {
// Der Identifier ist jetzt nur noch "ak" @Override
return "ak"; public @NotNull String getIdentifier() {
} return "ak";
}
@Override
public String getAuthor() { @Override
return "mviper"; public @NotNull String getAuthor() {
} return "M_Viper";
}
@Override
public String getVersion() { @Override
return "1.1.0"; public @NotNull String getVersion() {
} return "2.0.0";
}
@Override
public boolean canRegister() { @Override
return true; public boolean persist() {
} return true; // Expansion bleibt nach PAPI-Reload registriert
}
@Override
public String onPlaceholderRequest(Player player, String identifier) { @Override
if (player == null) { public String onPlaceholderRequest(Player player, String identifier) {
return ""; if (player == null) return "";
}
// Aktuellen Tag ermitteln (Berücksichtigt den test_day aus der Config)
// Die Platzhalter sind jetzt viel kürzer int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0);
switch (identifier) { LocalDate date = LocalDate.now();
case "c": // Geöffnet (claimed) int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0);
return String.valueOf(CalendarData.getClaimedCount(player));
switch (identifier.toLowerCase()) {
case "n": // Nächstes (next)
for (int day = 1; day <= 24; day++) { // %ak_claimed% - Anzahl der bereits geöffneten Türchen
if (!CalendarData.hasClaimed(player, day)) { case "claimed":
return String.valueOf(day); return String.valueOf(DataHandler.getClaimedDays(player).size());
}
} // %ak_day% - Der aktuelle Tag des Adventskalenders (1-24)
return "None"; case "day":
return String.valueOf(currentDay);
case "d": // Tage (days)
LocalDate today = LocalDate.now(); // %ak_has_claimed_today% - Gibt "Ja" oder "Nein" zurück, ob heute schon geöffnet wurde
LocalDate christmas = LocalDate.of(today.getYear(), Month.DECEMBER, 25); case "has_claimed_today":
if (today.isAfter(christmas)) { if (currentDay == 0) return "Keine Adventszeit";
christmas = christmas.plusYears(1); return DataHandler.hasClaimed(player, currentDay) ? "Ja" : "Nein";
}
return String.valueOf((int) java.time.temporal.ChronoUnit.DAYS.between(today, christmas)); // %ak_remaining% - Wie viele Türchen sind noch offen (für diesen Spieler)?
case "remaining":
default: int claimedCount = DataHandler.getClaimedDays(player).size();
return null; return String.valueOf(24 - claimedCount);
}
} // %ak_is_global% - Zeigt an, ob der globale Modus aktiv ist
case "is_global":
boolean isGlobal = Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar", false);
return isGlobal ? "Global" : "Spieler-basiert";
}
return null; // Placeholder unbekannt
}
} }

View File

@@ -1,87 +1,160 @@
package de.mviper.adventskalender; package de.mviper.adventskalender;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.command.*;
import org.bukkit.command.Command; import org.bukkit.entity.Player;
import org.bukkit.command.CommandExecutor; import java.util.*;
import org.bukkit.command.CommandSender; import java.util.stream.Collectors;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; public class AdventCommand implements CommandExecutor, TabCompleter {
public class AdventCommand implements CommandExecutor { @Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
@Override if (args.length > 0 && args[0].equalsIgnoreCase("admin")) {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!sender.hasPermission("adventskalender.admin")) {
sender.sendMessage(LanguageManager.getMessage("messages.no_permission"));
// --- Admin-Befehle (/ak admin ...) --- return true;
if (args.length >= 1 && args[0].equalsIgnoreCase("admin")) { }
// Prüfen, ob der Spieler die Admin-Berechtigung hat
if (!sender.hasPermission("adventskalender.admin")) { // /ak admin reload
sender.sendMessage(LanguageManager.getString("messages.no_permission")); if (args.length == 2 && args[1].equalsIgnoreCase("reload")) {
return true; Adventskalender.getInstance().reloadConfig();
} LanguageManager.setup();
DataHandler.setup();
// /ak admin reload sender.sendMessage(LanguageManager.getMessage("messages.admin.reload_success"));
if (args.length == 2 && args[1].equalsIgnoreCase("reload")) { return true;
Adventskalender.getInstance().reloadConfig(); }
LanguageManager.setup();
sender.sendMessage(LanguageManager.getString("messages.admin.reload_success")); // /ak admin test <Tag/reset>
return true; if (args.length == 3 && args[1].equalsIgnoreCase("test")) {
} if (args[2].equalsIgnoreCase("reset")) {
Adventskalender.getInstance().getConfig().set("general.test_day", 0);
// /ak admin open <player> <day> Adventskalender.getInstance().saveConfig();
if (args.length == 4 && args[1].equalsIgnoreCase("open")) { sender.sendMessage("§e[Admin] §7Test-Modus §cbeendet§7. Das echte Datum wird nun verwendet.");
Player target = Bukkit.getPlayer(args[2]); return true;
if (target == null) { }
sender.sendMessage(LanguageManager.getString("messages.admin.player_not_found").replace("%player%", args[2]));
return true; try {
} int day = Integer.parseInt(args[2]);
if (day < 1 || day > 24) {
try { sender.sendMessage("§cBitte gib einen Tag zwischen 1 und 24 an (oder 'reset').");
int day = Integer.parseInt(args[3]); return true;
if (day < 1 || day > 24) { }
sender.sendMessage(LanguageManager.getString("messages.admin.invalid_day")); Adventskalender.getInstance().getConfig().set("general.test_day", day);
return true; Adventskalender.getInstance().saveConfig();
} sender.sendMessage("§e[Admin] §7Test-Tag auf §6" + day + " §7gesetzt.");
} catch (NumberFormatException e) {
ItemStack reward = AdventInventory.getRewardItem(day); sender.sendMessage("§cBitte gib eine gültige Zahl (1-24) oder 'reset' an.");
if (reward != null && reward.getType() != Material.AIR) { }
if (target.getInventory().firstEmpty() == -1) { return true;
target.getWorld().dropItemNaturally(target.getLocation(), reward); }
} else {
target.getInventory().addItem(reward); // /ak admin resetday <global/Spieler> <Tag>
} if (args.length == 4 && args[1].equalsIgnoreCase("resetday")) {
} String target = args[2];
CalendarData.setClaimed(target, day); try {
int day = Integer.parseInt(args[3]);
sender.sendMessage(LanguageManager.getString("messages.admin.open_success").replace("%player%", target.getName()).replace("%day%", String.valueOf(day))); if (day < 1 || day > 24) throw new NumberFormatException();
target.sendMessage(LanguageManager.getString("messages.reward_received").replace("%day%", String.valueOf(day)));
if (target.equalsIgnoreCase("global")) {
} catch (NumberFormatException e) { DataHandler.GlobalDataManager.resetGlobalDay(day);
sender.sendMessage(LanguageManager.getString("messages.admin.invalid_day")); sender.sendMessage("§a[Admin] §7Tag §6" + day + " §7global zurückgesetzt.");
} } else {
return true; DataHandler.resetPlayerDay(target, day);
} sender.sendMessage("§a[Admin] §7Tag §6" + day + " §7für §e" + target + " §7zurückgesetzt.");
}
// Falsche Nutzung der Admin-Befehle } catch (NumberFormatException e) {
sender.sendMessage("§cUsage: /ak admin reload"); sender.sendMessage("§cTag muss zwischen 1 und 24 liegen.");
sender.sendMessage("§cUsage: /ak admin open <player> <day>"); }
return true; return true;
} }
// --- Hauptbefehl (/ak) --- // /ak admin reset <global/Spieler>
if (!(sender instanceof Player)) { if (args.length >= 3 && args[1].equalsIgnoreCase("reset")) {
sender.sendMessage(LanguageManager.getString("messages.only_player")); String baseTitle = LanguageManager.getString("gui.title");
return true; if (args[2].equalsIgnoreCase("global")) {
} DataHandler.resetAll();
for (Player online : Bukkit.getOnlinePlayers()) {
Player player = (Player) sender; if (online.getOpenInventory().getTitle().contains(baseTitle)) online.closeInventory();
if (!player.hasPermission("adventskalender.use")) { }
player.sendMessage(LanguageManager.getString("messages.no_permission")); sender.sendMessage("§a[Admin] §7Kompletter Reset durchgeführt.");
return true; } else {
} String targetName = args[2];
Player target = Bukkit.getPlayer(targetName);
AdventInventory.openCalendar(player); if (target != null) {
return true; DataHandler.resetPlayer(target);
} if (target.getOpenInventory().getTitle().contains(baseTitle)) target.closeInventory();
sender.sendMessage("§a[Admin] §7Daten für §e" + target.getName() + " §7gelöscht.");
} else {
DataHandler.resetOfflinePlayer(targetName);
sender.sendMessage("§a[Admin] §7Offline-Daten für §e" + targetName + " §7gelöscht.");
}
}
return true;
}
sender.sendMessage("§8§m---§r §6Advent Admin §8§m---");
sender.sendMessage("§e/ak admin reload §7- Config neu laden");
sender.sendMessage("§e/ak admin test <1-24/reset> §7- Datum simulieren");
sender.sendMessage("§e/ak admin resetday <global/Spieler> <Tag> §7- Tag löschen");
sender.sendMessage("§e/ak admin reset <global/Spieler> §7- Alles löschen");
return true;
}
if (!(sender instanceof Player player)) return true;
if (!player.hasPermission("adventskalender.use")) {
player.sendMessage(LanguageManager.getMessage("messages.no_permission"));
return true;
}
AdventInventory.open(player);
return true;
}
@Override
public List<String> onTabComplete(CommandSender s, Command c, String a, String[] args) {
if (args.length == 1) return Collections.singletonList("admin");
if (args.length == 2 && args[0].equalsIgnoreCase("admin"))
return Arrays.asList("reload", "test", "reset", "resetday");
// Tab-Completion für das Ziel (global oder Spielername)
if (args.length == 3 && (args[1].equalsIgnoreCase("reset") || args[1].equalsIgnoreCase("resetday"))) {
List<String> options = new ArrayList<>();
options.add("global");
Bukkit.getOnlinePlayers().forEach(p -> options.add(p.getName()));
return options.stream()
.filter(opt -> opt.toLowerCase().startsWith(args[2].toLowerCase()))
.collect(Collectors.toList());
}
// Tab-Completion für die TAGE (Numerisch sortiert)
if ((args.length == 3 && args[1].equalsIgnoreCase("test")) || (args.length == 4 && args[1].equalsIgnoreCase("resetday"))) {
List<String> options = new ArrayList<>();
// "reset" Option nur für den test command hinzufügen
if (args[1].equalsIgnoreCase("test")) {
options.add("reset");
}
for (int i = 1; i <= 24; i++) {
options.add(String.valueOf(i));
}
String currentInput = (args.length == 3) ? args[2] : args[3];
return options.stream()
.filter(opt -> opt.toLowerCase().startsWith(currentInput.toLowerCase()))
.sorted((s1, s2) -> {
// "reset" immer nach oben sortieren
if (s1.equalsIgnoreCase("reset")) return -1;
if (s2.equalsIgnoreCase("reset")) return 1;
// Restliche Zahlen numerisch vergleichen
return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2));
})
.collect(Collectors.toList());
}
return Collections.emptyList();
}
} }

View File

@@ -3,116 +3,152 @@ package de.mviper.adventskalender;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.profile.PlayerProfile;
import org.bukkit.profile.PlayerTextures;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Month; import java.time.Month;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
public class AdventInventory { public class AdventInventory {
public static void openCalendar(org.bukkit.entity.Player player) { // Textur für das geöffnete Geschenk (Wreath)
String title = LanguageManager.getString("gui.title"); private static final String OPEN_WREATH_URL = "http://textures.minecraft.net/texture/5e39248b341c87ce3e4294ac214c6f74468889cb1273ae7412906fd28db097e8";
// Texturen für die geschlossenen Tage (1-24)
private static final Map<Integer, String> CLOSED_TEXTURES = new HashMap<>();
private static final Map<String, ItemStack> HEAD_CACHE = new HashMap<>();
static {
// Hier sind die URLs für die "Christmas Calendar #01-24 (red)" Reihe
CLOSED_TEXTURES.put(1, "http://textures.minecraft.net/texture/105b356be68ac56cfe0611b95027adf1ee67050e4cd66a0a99678fc2e25b6b0f");
CLOSED_TEXTURES.put(2, "http://textures.minecraft.net/texture/40ec45e8ffb9cb714d1412bb9ee54fd1e7a3140c02e35ae0d3f308f1666ba126");
CLOSED_TEXTURES.put(3, "http://textures.minecraft.net/texture/2466629a31aae22dba15a7b710a4517308656c19ba39a89ac2de2915a46e2823");
CLOSED_TEXTURES.put(4, "http://textures.minecraft.net/texture/97a1956ed6f32e18b139f2d709355ac6945d0ab8fecd04d4c159d08006bfbe58");
CLOSED_TEXTURES.put(5, "http://textures.minecraft.net/texture/855fad012c8844fc313510436b4919e002d93d6a0761f7bdaf78069683eee5e6");
CLOSED_TEXTURES.put(6, "http://textures.minecraft.net/texture/c0400111ae026101077b59d8482c84a832c74f4f2105fc13b7c94f5b8000ea3");
CLOSED_TEXTURES.put(7, "http://textures.minecraft.net/texture/a34505ed2567ea2efbab49977f7da8f1178db9ae53a0e99737286dc56e63636a");
CLOSED_TEXTURES.put(8, "http://textures.minecraft.net/texture/a17ddf12485fb54cab9c9ffb55feea62a2d90b825772c6ea38561ce750cb6f83");
CLOSED_TEXTURES.put(9, "http://textures.minecraft.net/texture/f75022feb11b736e50bc7d19d26d4ee00cecd6734a2138d2579f799b119b6734");
CLOSED_TEXTURES.put(10, "http://textures.minecraft.net/texture/6b84208e4be2233fcd96e7f02d9613d5437f1d006236277c57d40652c5be2f23");
CLOSED_TEXTURES.put(11, "http://textures.minecraft.net/texture/19aa8a65ba29b4b7cbb9c66cf59fb537ba2c3013a958620b976b9f06248e8dda");
CLOSED_TEXTURES.put(12, "http://textures.minecraft.net/texture/7dc1330b302e800c31c8dfefbd1b996016c34649f3a396749d3e048637364aad");
CLOSED_TEXTURES.put(13, "http://textures.minecraft.net/texture/7e98a3884e0eac967c73a2bcbff7df62ab2fb327b4ceb9e636c74254866b5c7c");
CLOSED_TEXTURES.put(14, "http://textures.minecraft.net/texture/3866643decb2d148999f9768dae89fbdb196b30d55f8846b1fbbe736bb7e5042");
CLOSED_TEXTURES.put(15, "http://textures.minecraft.net/texture/744a95611950cc7f154c9f56ad472c1941376ec171efb8ba476e6cec4f866526");
CLOSED_TEXTURES.put(16, "http://textures.minecraft.net/texture/1546959556eff4b8827ca50d8df686e8f20d5c843da689dddc48bff0a217efbe");
CLOSED_TEXTURES.put(17, "http://textures.minecraft.net/texture/1f4fb00e824c97d9cfba4c44def27a79513978979515048f2e69eb5b7123c159");
CLOSED_TEXTURES.put(18, "http://textures.minecraft.net/texture/9216b42018004a86081b5c1ee87dc8f12770f1859266e2cd359b75b44b5e7680");
CLOSED_TEXTURES.put(19, "http://textures.minecraft.net/texture/93c7097291042c394267892c56feee9ba3936826ea5b2232f46cb5474f9cd937");
CLOSED_TEXTURES.put(20, "http://textures.minecraft.net/texture/4f85a13977d01865a9d95d3279528285872551c36fd5231c7e3ab9897a7560fd");
CLOSED_TEXTURES.put(21, "http://textures.minecraft.net/texture/cdb512403f3610966d10bd3ca9fc61fdc5021268382f69719ac5679f3efd4c14");
CLOSED_TEXTURES.put(22, "http://textures.minecraft.net/texture/cadaef824771250374fc3e82efe020ab765e665340cc2393f3b4ae8d6d18529c");
CLOSED_TEXTURES.put(23, "http://textures.minecraft.net/texture/266f896c37a9d7fb857ff8c9f6f5e3540d17e1b893419f523eb199b71e71dde3");
CLOSED_TEXTURES.put(24, "http://textures.minecraft.net/texture/f081f9274e4ef802453ac95381c00426e462440d3bb085fb3afefa6ded6a8d01");
// Hinweis: In der Praxis füllst du hier alle 1-24 mit den URLs von Minecraft-Heads.
}
public static void initCache() {
// Cache den Wreath-Kopf
HEAD_CACHE.put("OPENED", createBaseSkull(OPEN_WREATH_URL));
// Cache alle nummerierten geschlossenen Köpfe
for (int i = 1; i <= 24; i++) {
if (CLOSED_TEXTURES.containsKey(i)) {
HEAD_CACHE.put("CLOSED_" + i, createBaseSkull(CLOSED_TEXTURES.get(i)));
}
}
}
public static void open(Player player) {
String title = " " + LanguageManager.getString("gui.title");
Inventory inv = Bukkit.createInventory(null, 54, title); Inventory inv = Bukkit.createInventory(null, 54, title);
for (int i = 0; i < 24; i++) { int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0);
int day = i + 1; LocalDate date = LocalDate.now();
ItemStack item = getCalendarItem(player, day); int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0);
inv.setItem(i, item);
for (int i = 0; i < 54; i++) {
if (i < 9 || i >= 45 || i % 9 == 0 || (i + 1) % 9 == 0) {
Material m = (i % 2 == 0) ? Material.RED_STAINED_GLASS_PANE : Material.GREEN_STAINED_GLASS_PANE;
inv.setItem(i, createFiller(m));
} else {
inv.setItem(i, createFiller(Material.WHITE_STAINED_GLASS_PANE));
}
} }
ItemStack filler = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); int[] slots = {10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 39, 40, 41};
ItemMeta fillerMeta = filler.getItemMeta();
fillerMeta.setDisplayName(" "); for (int day = 1; day <= 24; day++) {
filler.setItemMeta(fillerMeta); boolean claimed = DataHandler.hasClaimed(player, day);
for (int i = 24; i < 54; i++) { boolean isAvailable = currentDay >= day;
inv.setItem(i, filler); inv.setItem(slots[day - 1], createGiftItem(day, claimed, isAvailable));
} }
player.openInventory(inv); player.openInventory(inv);
} }
public static ItemStack getCalendarItem(org.bukkit.entity.Player player, int day) { private static ItemStack createGiftItem(int day, boolean claimed, boolean isAvailable) {
LocalDate today = LocalDate.now(); ItemStack item;
String prefix = LanguageManager.getString("prefix"); if (claimed) {
item = HEAD_CACHE.getOrDefault("OPENED", new ItemStack(Material.PLAYER_HEAD)).clone();
if (today.getMonth() != Month.DECEMBER || today.getDayOfMonth() < day) { } else {
ItemStack item = new ItemStack(Material.BARRIER); item = HEAD_CACHE.getOrDefault("CLOSED_" + day, new ItemStack(Material.PLAYER_HEAD)).clone();
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(prefix + "§cTag " + day);
List<String> lore = new ArrayList<>();
lore.add(prefix + "§7Noch nicht verfügbar.");
lore.add(prefix + "§7Komme am " + day + ". Dezember wieder.");
meta.setLore(lore);
item.setItemMeta(meta);
return item;
} }
if (CalendarData.hasClaimed(player, day)) {
ItemStack item = new ItemStack(Material.GREEN_STAINED_GLASS_PANE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(prefix + "§aTag " + day + " - Geöffnet");
List<String> lore = new ArrayList<>();
lore.add(prefix + LanguageManager.getString("messages.day_already_claimed"));
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
ItemStack item = new ItemStack(Material.CHEST);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
meta.setDisplayName(prefix + "§eTag " + day + " - Noch zu holen!");
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
lore.add(prefix + "§7Klicke, um dein Geschenk zu erhalten."); lore.add(" ");
if (claimed) {
meta.setDisplayName(LanguageManager.getString("rewards.day_" + day + ".name"));
meta.addEnchant(Enchantment.LUCK, 1, true);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
lore.add("§8» §6Bereits abgeholt");
lore.add("§aDu hast dieses Geschenk bereits geöffnet! ✔");
} else {
if (!isAvailable) {
meta.setDisplayName("§c§lTag #" + day);
lore.add("§8» §7Noch verschlossen");
lore.add("§7Komme am " + day + ". Dezember wieder!");
} else {
meta.setDisplayName(LanguageManager.getString("rewards.day_" + day + ".name"));
lore.add("§8» §eKlicke zum Öffnen!");
lore.addAll(LanguageManager.getStringList("rewards.day_" + day + ".lore"));
}
}
meta.setLore(lore); meta.setLore(lore);
item.setItemMeta(meta); item.setItemMeta(meta);
return item; return item;
} }
public static ItemStack getRewardItem(int day) { private static ItemStack createBaseSkull(String url) {
String path = "rewards." + day; ItemStack item = new ItemStack(Material.PLAYER_HEAD);
if (!Adventskalender.getInstance().getConfig().contains(path)) { SkullMeta meta = (SkullMeta) item.getItemMeta();
return new ItemStack(Material.AIR); try {
} UUID uuid = UUID.nameUUIDFromBytes(url.getBytes());
PlayerProfile profile = Bukkit.createPlayerProfile(uuid, "AdventHead");
profile.getTextures().setSkin(new URL(url));
meta.setOwnerProfile(profile);
item.setItemMeta(meta);
} catch (MalformedURLException e) { e.printStackTrace(); }
return item;
}
String materialName = Adventskalender.getInstance().getConfig().getString(path + ".material"); private static ItemStack createFiller(Material material) {
int amount = Adventskalender.getInstance().getConfig().getInt(path + ".amount"); ItemStack item = new ItemStack(material);
String key = Adventskalender.getInstance().getConfig().getString(path + ".key");
Material material = Material.getMaterial(materialName);
if (material == null) {
Adventskalender.getInstance().getLogger().warning("Material '" + materialName + "' für Tag " + day + " nicht gefunden!");
return new ItemStack(Material.BARRIER);
}
ItemStack item = new ItemStack(material, amount);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta != null) { meta.setDisplayName(" "); item.setItemMeta(meta); }
String namePath = key + ".name";
String lorePath = key + ".lore";
meta.setDisplayName(LanguageManager.getString(namePath));
meta.setLore(LanguageManager.getStringList(lorePath));
List<Map<?, ?>> enchantmentList = Adventskalender.getInstance().getConfig().getMapList(path + ".enchantments");
if (enchantmentList != null) {
for (Map<?, ?> enchantmentMap : enchantmentList) {
String enchantName = (String) enchantmentMap.get("type");
int level = (int) enchantmentMap.get("level");
Enchantment enchantment = Enchantment.getByName(enchantName);
if (enchantment != null) {
meta.addEnchant(enchantment, level, true);
}
}
}
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
item.setItemMeta(meta);
return item; return item;
} }
} }

View File

@@ -1,56 +1,135 @@
package de.mviper.adventskalender; package de.mviper.adventskalender;
import org.bukkit.*; import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Month; import java.time.Month;
import java.util.List;
import java.util.Map;
public class AdventListener implements Listener { public class AdventListener implements Listener {
@EventHandler @EventHandler
public void onInventoryClick(InventoryClickEvent event) { public void onJoin(PlayerJoinEvent event) {
String translatedTitle = LanguageManager.getString("gui.title"); Player player = event.getPlayer();
// Nachricht an Admins senden, wenn ein Update da ist
if (player.hasPermission("adventskalender.admin")) {
// Delay von 2 Sekunden (40 Ticks), damit die Nachricht nicht im Join-Spam untergeht
Bukkit.getScheduler().runTaskLater(Adventskalender.getInstance(), () -> {
if (Adventskalender.getInstance().isUpdateAvailable()) {
String latest = Adventskalender.getInstance().getLatestVersion();
String current = Adventskalender.getInstance().getDescription().getVersion();
if (event.getView().getTitle().equals(translatedTitle)) { player.sendMessage("§7 ");
event.setCancelled(true); player.sendMessage("§8§m+-------------------------------------------+");
player.sendMessage("§6§l ADVENTSKALENDER UPDATE");
player.sendMessage("§7 ");
player.sendMessage("§7 Eine neue Version ist verfügbar: §a§l" + latest);
player.sendMessage("§7 Installierte Version: §c" + current);
player.sendMessage("§7 ");
if (!(event.getWhoClicked() instanceof Player)) return; // Erstellung der klickbaren Nachricht
Player player = (Player) event.getWhoClicked(); TextComponent message = new TextComponent("§e §nKLICKE HIER ZUM DOWNLOAD");
message.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/130974/"));
message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("§7Öffnet die Spigot-Seite")));
int slot = event.getSlot(); player.spigot().sendMessage(message);
if (slot < 0 || slot > 23) return;
int day = slot + 1; player.sendMessage("§7 ");
LocalDate today = LocalDate.now(); player.sendMessage("§8§m+-------------------------------------------+");
player.sendMessage("§7 ");
if (today.getMonth() == Month.DECEMBER && today.getDayOfMonth() >= day && !CalendarData.hasClaimed(player, day)) {
ItemStack reward = AdventInventory.getRewardItem(day);
if (reward != null && reward.getType() != Material.AIR) {
if (player.getInventory().firstEmpty() == -1) {
player.getWorld().dropItemNaturally(player.getLocation(), reward);
player.sendMessage(LanguageManager.getString("messages.inventory_full"));
} else {
player.getInventory().addItem(reward);
}
player.sendMessage(LanguageManager.getString("messages.reward_received").replace("%day%", String.valueOf(day)));
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f);
player.spawnParticle(Particle.VILLAGER_HAPPY, player.getLocation().add(0, 1, 0), 20, 0.5, 0.5, 0.5, 0);
} }
}, 40L);
CalendarData.setClaimed(player, day);
ItemStack newItem = AdventInventory.getCalendarItem(player, day);
event.getInventory().setItem(slot, newItem);
} else {
player.sendMessage(LanguageManager.getString("messages.day_not_available"));
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1.0f, 1.0f);
}
} }
} }
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
String baseTitle = LanguageManager.getString("gui.title");
if (!event.getView().getTitle().contains(baseTitle)) return;
event.setCancelled(true);
if (!(event.getWhoClicked() instanceof Player player)) return;
ItemStack clicked = event.getCurrentItem();
if (clicked == null || clicked.getType() != Material.PLAYER_HEAD) return;
if (!clicked.hasItemMeta() || !clicked.getItemMeta().hasDisplayName()) return;
String displayName = ChatColor.stripColor(clicked.getItemMeta().getDisplayName());
int day;
try {
day = Integer.parseInt(displayName.replaceAll("[^0-9]", ""));
} catch (NumberFormatException e) {
return;
}
int testDay = Adventskalender.getInstance().getConfig().getInt("general.test_day", 0);
LocalDate date = LocalDate.now();
int currentDay = (testDay > 0) ? testDay : (date.getMonth() == Month.DECEMBER ? date.getDayOfMonth() : 0);
if (currentDay >= day) {
if (DataHandler.hasClaimed(player, day)) {
player.sendMessage(LanguageManager.getMessage("messages.day_already_claimed"));
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f);
return;
}
giveReward(player, day);
DataHandler.setClaimed(player, day);
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.5f);
AdventInventory.open(player);
} else {
player.sendMessage(LanguageManager.getMessage("messages.day_not_available"));
player.playSound(player.getLocation(), Sound.BLOCK_CHEST_LOCKED, 1f, 1f);
}
}
private void giveReward(Player player, int day) {
String path = "rewards." + day;
String matName = Adventskalender.getInstance().getConfig().getString(path + ".material", "COOKIE");
int amount = Adventskalender.getInstance().getConfig().getInt(path + ".amount", 1);
ItemStack reward = new ItemStack(Material.valueOf(matName.toUpperCase()), amount);
ItemMeta meta = reward.getItemMeta();
if (meta != null) {
List<Map<?, ?>> enchants = Adventskalender.getInstance().getConfig().getMapList(path + ".enchantments");
for (Map<?, ?> entry : enchants) {
String type = (String) entry.get("type");
Object levelObj = entry.get("level");
int level = (levelObj instanceof Integer) ? (int) levelObj : 1;
Enchantment enc = Enchantment.getByName(type.toUpperCase());
if (enc != null) {
meta.addEnchant(enc, level, true);
}
}
reward.setItemMeta(meta);
}
if (player.getInventory().firstEmpty() == -1) {
player.getWorld().dropItemNaturally(player.getLocation(), reward);
player.sendMessage(LanguageManager.getMessage("messages.inventory_full"));
} else {
player.getInventory().addItem(reward);
}
player.sendMessage(LanguageManager.getMessage("messages.reward_received").replace("%day%", String.valueOf(day)));
}
} }

View File

@@ -1,39 +1,62 @@
package de.mviper.adventskalender; package de.mviper.adventskalender;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
public final class Adventskalender extends JavaPlugin { public final class Adventskalender extends JavaPlugin {
private static Adventskalender instance; private static Adventskalender instance;
private NamespacedKey dataKey;
// Update-Status Variablen
private boolean updateAvailable = false;
private String latestVersion = "";
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
this.dataKey = new NamespacedKey(this, "claimed_days_v2");
saveDefaultConfig(); saveDefaultConfig();
LanguageManager.setup(); LanguageManager.setup();
CalendarData.setup();
// Initialisierung der Daten (MySQL wird hier bevorzugt behandelt)
DataHandler.setup();
// Textur-Puffer füllen, um Steve-Köpfe zu vermeiden
AdventInventory.initCache();
getCommand("adventskalender").setExecutor(new AdventCommand()); getCommand("adventskalender").setExecutor(new AdventCommand());
getServer().getPluginManager().registerEvents(new AdventListener(), this); getServer().getPluginManager().registerEvents(new AdventListener(), this);
// Prüfen, ob PlaceholderAPI vorhanden ist und unsere neue Erweiterung registrieren // Update Checker Integration (ID: 130974)
new UpdateChecker(this, 130974).getVersion(version -> {
if (!this.getDescription().getVersion().equals(version)) {
this.updateAvailable = true;
this.latestVersion = version;
getLogger().warning("Eine neue Version (" + version + ") ist verfügbar!");
getLogger().warning("Download: https://www.spigotmc.org/resources/130974/");
} else {
getLogger().info("Das Plugin ist auf dem neuesten Stand.");
}
});
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
new AdventCalendarExpansion().register(); new AdventCalendarExpansion().register();
getLogger().info("PlaceholderAPI expansion registered.");
} else {
getLogger().warning("PlaceholderAPI not found. Placeholders will not work.");
} }
getLogger().info("Adventskalender-Plugin wurde erfolgreich aktiviert!"); getLogger().info("Adventskalender erfolgreich gestartet!");
} }
@Override @Override
public void onDisable() { public void onDisable() {
getLogger().info("Adventskalender-Plugin wurde deaktiviert."); MySQLManager.close();
} }
public static Adventskalender getInstance() { public static Adventskalender getInstance() { return instance; }
return instance; public NamespacedKey getDataKey() { return dataKey; }
}
// Getter für den Update-Status
public boolean isUpdateAvailable() { return updateAvailable; }
public String getLatestVersion() { return latestVersion; }
} }

View File

@@ -1,68 +0,0 @@
package de.mviper.adventskalender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class CalendarData {
private static File file;
private static FileConfiguration customFile;
public static void setup() {
file = new File(Adventskalender.getInstance().getDataFolder(), "calendar.yml");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
Adventskalender.getInstance().getLogger().severe("Konnte calendar.yml nicht erstellen: " + e.getMessage());
}
}
customFile = YamlConfiguration.loadConfiguration(file);
}
public static void save() {
try {
customFile.save(file);
} catch (IOException e) {
Adventskalender.getInstance().getLogger().severe("Konnte calendar.yml nicht speichern: " + e.getMessage());
}
}
public static boolean hasClaimed(Player player, int day) {
boolean isGlobal = Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar");
if (isGlobal) {
List<Integer> claimedDays = customFile.getIntegerList("global_claimed_days");
return claimedDays.contains(day);
} else {
List<Integer> claimedDays = customFile.getIntegerList(player.getUniqueId().toString());
return claimedDays.contains(day);
}
}
public static void setClaimed(Player player, int day) {
boolean isGlobal = Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar");
if (isGlobal) {
List<Integer> claimedDays = customFile.getIntegerList("global_claimed_days");
claimedDays.add(day);
customFile.set("global_claimed_days", claimedDays);
} else {
List<Integer> claimedDays = customFile.getIntegerList(player.getUniqueId().toString());
claimedDays.add(day);
customFile.set(player.getUniqueId().toString(), claimedDays);
}
save();
}
public static int getClaimedCount(Player player) {
if (Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar")) {
return customFile.getIntegerList("global_claimed_days").size();
} else {
return customFile.getIntegerList(player.getUniqueId().toString()).size();
}
}
}

View File

@@ -0,0 +1,241 @@
package de.mviper.adventskalender;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
public class DataHandler {
private static File file;
private static FileConfiguration dataConfig;
private static boolean mysqlActive = false;
public static void setup() {
if (Adventskalender.getInstance().getConfig().getBoolean("mysql.enable")) {
MySQLManager.connect();
if (MySQLManager.isConnected()) {
mysqlActive = true;
Adventskalender.getInstance().getLogger().info("MySQL-Verbindung steht. data.yml wird deaktiviert.");
return;
} else {
Adventskalender.getInstance().getLogger().warning("MySQL aktiviert, aber Verbindung fehlgeschlagen! Nutze data.yml als Backup.");
}
}
if (!Adventskalender.getInstance().getDataFolder().exists()) {
Adventskalender.getInstance().getDataFolder().mkdirs();
}
file = new File(Adventskalender.getInstance().getDataFolder(), "data.yml");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
dataConfig = YamlConfiguration.loadConfiguration(file);
}
public static boolean hasClaimed(Player player, int day) {
if (isGlobal()) {
return GlobalDataManager.isDayClaimedGlobally(day);
}
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT 1 FROM advent_players WHERE uuid = ? AND day = ?")) {
ps.setString(1, player.getUniqueId().toString());
ps.setInt(2, day);
return ps.executeQuery().next();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (dataConfig != null) {
List<Integer> claimed = dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed");
return claimed.contains(day);
}
return false;
}
public static void setClaimed(Player player, int day) {
if (isGlobal()) {
GlobalDataManager.setDayClaimedGlobally(day, player.getName());
return;
}
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("INSERT IGNORE INTO advent_players (uuid, day) VALUES (?, ?)")) {
ps.setString(1, player.getUniqueId().toString());
ps.setInt(2, day);
ps.executeUpdate();
return;
} catch (SQLException e) {
e.printStackTrace();
}
}
if (dataConfig != null) {
List<Integer> claimed = dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed");
if (!claimed.contains(day)) {
claimed.add(day);
dataConfig.set("players." + player.getUniqueId() + ".claimed", claimed);
dataConfig.set("players." + player.getUniqueId() + ".name", player.getName());
save();
}
}
}
public static void resetPlayerDay(String playerName, int day) {
if (mysqlActive && MySQLManager.isConnected()) {
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(playerName);
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players WHERE uuid = ? AND day = ?")) {
ps.setString(1, target.getUniqueId().toString());
ps.setInt(2, day);
ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); }
}
if (dataConfig != null && dataConfig.getConfigurationSection("players") != null) {
for (String uuid : dataConfig.getConfigurationSection("players").getKeys(false)) {
if (playerName.equalsIgnoreCase(dataConfig.getString("players." + uuid + ".name"))) {
List<Integer> claimed = dataConfig.getIntegerList("players." + uuid + ".claimed");
claimed.remove(Integer.valueOf(day));
dataConfig.set("players." + uuid + ".claimed", claimed);
save();
break;
}
}
}
}
public static Set<Integer> getClaimedDays(Player player) {
if (mysqlActive && MySQLManager.isConnected()) {
Set<Integer> days = new HashSet<>();
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT day FROM advent_players WHERE uuid = ?")) {
ps.setString(1, player.getUniqueId().toString());
ResultSet rs = ps.executeQuery();
while (rs.next()) days.add(rs.getInt("day"));
return days;
} catch (SQLException e) {
e.printStackTrace();
}
}
if (dataConfig != null) {
return new HashSet<>(dataConfig.getIntegerList("players." + player.getUniqueId() + ".claimed"));
}
return new HashSet<>();
}
public static void resetPlayer(Player player) {
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players WHERE uuid = ?")) {
ps.setString(1, player.getUniqueId().toString());
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (dataConfig != null) {
dataConfig.set("players." + player.getUniqueId(), null);
save();
}
}
public static void resetOfflinePlayer(String name) {
if (dataConfig == null || dataConfig.getConfigurationSection("players") == null) return;
for (String uuid : dataConfig.getConfigurationSection("players").getKeys(false)) {
String savedName = dataConfig.getString("players." + uuid + ".name");
if (name.equalsIgnoreCase(savedName)) {
dataConfig.set("players." + uuid, null);
save();
break;
}
}
}
public static void resetAll() {
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps1 = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_players");
PreparedStatement ps2 = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_global")) {
ps1.executeUpdate();
ps2.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (dataConfig != null) {
dataConfig.set("players", null);
dataConfig.set("global", null);
save();
}
}
private static boolean isGlobal() {
return Adventskalender.getInstance().getConfig().getBoolean("calendar.use_global_calendar", false);
}
private static void save() {
if (dataConfig == null || file == null) return;
try {
dataConfig.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
public static class GlobalDataManager {
public static boolean isDayClaimedGlobally(int day) {
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("SELECT 1 FROM advent_global WHERE day = ?")) {
ps.setInt(1, day);
return ps.executeQuery().next();
} catch (SQLException e) {
e.printStackTrace();
}
}
return dataConfig != null && dataConfig.contains("global.day_" + day);
}
public static void setDayClaimedGlobally(int day, String playerName) {
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("REPLACE INTO advent_global (day, player_name) VALUES (?, ?)")) {
ps.setInt(1, day);
ps.setString(2, playerName);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
} else if (dataConfig != null) {
dataConfig.set("global.day_" + day, playerName);
save();
}
}
public static void resetGlobalDay(int day) {
if (mysqlActive && MySQLManager.isConnected()) {
try (PreparedStatement ps = MySQLManager.getConnection().prepareStatement("DELETE FROM advent_global WHERE day = ?")) {
ps.setInt(1, day);
ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); }
} else if (dataConfig != null) {
dataConfig.set("global.day_" + day, null);
DataHandler.save();
}
}
}
}

View File

@@ -1,41 +1,35 @@
package de.mviper.adventskalender; package de.mviper.adventskalender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.ChatColor;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.File; import java.util.List;
import java.util.List; import java.util.stream.Collectors;
public class LanguageManager { public class LanguageManager {
private static YamlConfiguration cfg;
private static FileConfiguration languageConfig; private static String prefix;
public static void setup() { public static void setup() {
String lang = Adventskalender.getInstance().getConfig().getString("general.language", "de"); String lang = Adventskalender.getInstance().getConfig().getString("general.language", "de");
File languageFile = new File(Adventskalender.getInstance().getDataFolder(), "messages_" + lang + ".yml"); File f = new File(Adventskalender.getInstance().getDataFolder(), "messages_" + lang + ".yml");
if (!f.exists()) Adventskalender.getInstance().saveResource("messages_" + lang + ".yml", false);
if (!languageFile.exists()) { cfg = YamlConfiguration.loadConfiguration(f);
Adventskalender.getInstance().saveResource("messages_" + lang + ".yml", false); prefix = ChatColor.translateAlternateColorCodes('&', cfg.getString("prefix", "&6[Advent] "));
} }
languageConfig = YamlConfiguration.loadConfiguration(languageFile); public static String getString(String path) {
} String s = cfg.getString(path);
return s == null ? "§cKey error: " + path : ChatColor.translateAlternateColorCodes('&', s);
public static String getString(String path) { }
if (languageConfig.contains(path)) {
return format(languageConfig.getString(path)); public static String getMessage(String path) {
} return prefix + getString(path);
return "§cLanguage key not found: " + path; }
}
public static List<String> getStringList(String path) {
public static List<String> getStringList(String path) { return cfg.getStringList(path).stream()
if (languageConfig.contains(path)) { .map(s -> ChatColor.translateAlternateColorCodes('&', s))
return languageConfig.getStringList(path).stream().map(LanguageManager::format).toList(); .collect(Collectors.toList());
} }
return List.of("§cLanguage key list not found: " + path);
}
private static String format(String message) {
return org.bukkit.ChatColor.translateAlternateColorCodes('&', message);
}
} }

View File

@@ -0,0 +1,66 @@
package de.mviper.adventskalender;
import org.bukkit.configuration.file.FileConfiguration;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MySQLManager {
private static Connection connection;
public static void connect() {
FileConfiguration config = Adventskalender.getInstance().getConfig();
String host = config.getString("mysql.host");
int port = config.getInt("mysql.port");
String database = config.getString("mysql.database");
String user = config.getString("mysql.user");
String password = config.getString("mysql.password");
try {
if (connection != null && !connection.isClosed()) return;
synchronized (MySQLManager.class) {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true&useSSL=false", user, password);
}
createTables();
Adventskalender.getInstance().getLogger().info("MySQL-Verbindung erfolgreich hergestellt!");
} catch (Exception e) {
Adventskalender.getInstance().getLogger().severe("MySQL-Verbindung fehlgeschlagen: " + e.getMessage());
}
}
private static void createTables() {
try (Statement s = connection.createStatement()) {
// Tabelle für Einzelspieler-Daten
s.executeUpdate("CREATE TABLE IF NOT EXISTS advent_players (uuid VARCHAR(36), day INT, PRIMARY KEY(uuid, day))");
// Tabelle für globalen Modus
s.executeUpdate("CREATE TABLE IF NOT EXISTS advent_global (day INT PRIMARY KEY, player_name VARCHAR(16))");
} catch (SQLException e) {
e.printStackTrace();
}
}
public static boolean isConnected() {
try {
return connection != null && !connection.isClosed();
} catch (SQLException e) {
return false;
}
}
public static void close() {
try {
if (isConnected()) connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
return connection;
}
}

View File

@@ -1,75 +0,0 @@
package de.mviper.adventskalender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Verwaltet die Spielerdaten in einer separaten YAML-Datei.
* Speichert, welcher Spieler an welchem Tag sein Geschenk bereits geholt hat.
*/
public class PlayerData {
private static File file;
private static FileConfiguration customFile;
/**
* Erstellt die playerdata.yml-Datei, falls sie nicht existiert.
*/
public static void setup() {
file = new File(Adventskalender.getInstance().getDataFolder(), "playerdata.yml");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
Adventskalender.getInstance().getLogger().severe("Konnte playerdata.yml nicht erstellen: " + e.getMessage());
}
}
customFile = YamlConfiguration.loadConfiguration(file);
}
public static FileConfiguration get() {
return customFile;
}
public static void save() {
try {
customFile.save(file);
} catch (IOException e) {
Adventskalender.getInstance().getLogger().severe("Konnte playerdata.yml nicht speichern: " + e.getMessage());
}
}
public static void reload() {
customFile = YamlConfiguration.loadConfiguration(file);
}
/**
* Prüft, ob ein Spieler ein Geschenk für einen bestimmten Tag bereits geholt hat.
* @param uuid Die UUID des Spielers
* @param day Der Tag (1-24)
* @return true, wenn bereits geholt, sonst false
*/
public static boolean hasClaimed(UUID uuid, int day) {
// Wir speichern eine Liste der geöffneten Tage für jeden Spieler
List<Integer> claimedDays = customFile.getIntegerList(uuid.toString());
return claimedDays.contains(day);
}
/**
* Markiert einen Tag für einen Spieler als geholt.
* @param uuid Die UUID des Spielers
* @param day Der Tag (1-24)
*/
public static void setClaimed(UUID uuid, int day) {
List<Integer> claimedDays = customFile.getIntegerList(uuid.toString());
claimedDays.add(day);
customFile.set(uuid.toString(), claimedDays);
save();
}
}

View File

@@ -0,0 +1,33 @@
package de.mviper.adventskalender;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
import java.util.function.Consumer;
public 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(this.plugin, () -> {
try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream();
Scanner scann = new Scanner(is)) {
if (scann.hasNext()) {
consumer.accept(scann.next());
}
} catch (IOException e) {
plugin.getLogger().info("Update-Check fehlgeschlagen: " + e.getMessage());
}
});
}
}

View File

@@ -8,6 +8,9 @@ general:
# Verfügbare Optionen: 'de', 'en' # Verfügbare Optionen: 'de', 'en'
language: "de" language: "de"
# Nur für Testzwecke: Simuliert einen Tag (0 = echtes Datum verwenden)
test_day: 0
# --- Kalender-Einstellungen --- # --- Kalender-Einstellungen ---
calendar: calendar:
# Soll jeder Spieler seinen eigenen, individuellen Kalender haben? # Soll jeder Spieler seinen eigenen, individuellen Kalender haben?
@@ -19,6 +22,15 @@ calendar:
# Dieser Text ist ein Schlüssel, der in der messages.yml übersetzt wird. # Dieser Text ist ein Schlüssel, der in der messages.yml übersetzt wird.
gui_title_key: "adventskalender.gui_title" gui_title_key: "adventskalender.gui_title"
# Datenbank für Bungeecord Nutzung
mysql:
enable: false
host: "localhost"
port: 3306
database: "adventskalender"
user: "root"
password: ""
# --- Belohnungen für die Tage 1 bis 24 --- # --- Belohnungen für die Tage 1 bis 24 ---
# Hier definierst du die Items für jeden Tag. # Hier definierst du die Items für jeden Tag.
# 'material': Der Materialname des Items (z.B. DIAMOND, ELYTRA). # 'material': Der Materialname des Items (z.B. DIAMOND, ELYTRA).

View File

@@ -1,15 +1,14 @@
# ============================================================================= # =============================================================================
# Adventskalender Plugin - Deutsche Sprachdatei # Adventskalender Plugin - Deutsche Sprachdatei (v2.0 - 2026)
# ============================================================================= # =============================================================================
# --- Prefix --- # --- Prefix ---
# Dieser Prefix wird vor fast jeder Nachricht im Chat angezeigt.
prefix: "&6[Adventskalender] &r" prefix: "&6[Adventskalender] &r"
# --- GUI-Texte --- # --- GUI-Texte ---
gui: gui:
# Der Titel des Adventskalender-Inventars. # Der Titel des Adventskalender-Inventars.
title: "&6✦ Adventskalender 2024 ✦" title: "&6✦ Adventskalender 2026 ✦"
# --- Nachrichten an Spieler --- # --- Nachrichten an Spieler ---
# Verfügbare Platzhalter: # Verfügbare Platzhalter:
@@ -18,90 +17,92 @@ gui:
messages: messages:
no_permission: "&cDafür hast du keine Berechtigung." no_permission: "&cDafür hast du keine Berechtigung."
only_player: "&cDieser Befehl kann nur von einem Spieler ausgeführt werden." only_player: "&cDieser Befehl kann nur von einem Spieler ausgeführt werden."
inventory_full: "&cDein Inventar war voll! Das Item wurde auf den Boden geworfen." inventory_full: "&cDein Inventar ist voll! Dein Geschenk wurde vor deine Füße geworfen."
reward_received: "&aDu hast dein Geschenk für Tag %day% erhalten!" reward_received: "&aGlückwunsch! Du hast dein Geschenk für Tag &e%day% &aerhalten!"
day_not_available: "&cDieses Geschenk kannst du (noch) nicht öffnen." day_not_available: "&cDieses Türchen ist noch fest verschlossen. Gedulde dich noch etwas!"
day_already_claimed: "&7Du hast dein Geschenk für diesen Tag bereits geholt." day_already_claimed: "&7Du hast dieses Geschenk bereits abgeholt."
global_already_claimed: "&cDieses Geschenk wurde heute bereits von jemand anderem abgeholt!"
# --- Admin-Nachrichten --- # --- Admin-Nachrichten ---
admin: admin:
reload_success: "&aKonfiguration und Sprachdateien wurden neu geladen." reload_success: "&aKonfiguration und Sprachdateien wurden erfolgreich neu geladen."
test_mode_on: "&e[Admin] &7Der Test-Modus wurde auf Tag &6%day% &7gesetzt."
test_mode_off: "&e[Admin] &7Test-Modus deaktiviert. Es wird wieder das reale Datum genutzt."
player_not_found: "&cDer Spieler '%player%' wurde nicht gefunden." player_not_found: "&cDer Spieler '%player%' wurde nicht gefunden."
invalid_day: "&cUngültiger Tag. Bitte wähle eine Zahl zwischen 1 und 24." invalid_day: "&cUngültiger Tag. Bitte wähle eine Zahl zwischen 1 und 24."
open_success: "&aDu hast für %player% das Türchen für Tag %day% geöffnet."
# --- Namen und Beschreibungen für die Belohnungs-Items --- # --- Belohnungs-Texte im GUI ---
# Die Schlüssel hier (z.B. 'day_1') müssen mit den 'keys' in der config.yml übereinstimmen. # Diese Schlüssel werden vom AdventInventory genutzt, um die Items im Menü zu benennen.
rewards: rewards:
day_1: day_1:
name: "&bErste Diamanten!" name: "&bTag 1: Erste Diamanten!"
lore: ["&7Ein kleiner Vorgeschmack auf den Reichtum...", "&7Viel Spaß damit!"] lore: ["&7Ein kleiner Vorgeschmack auf den Reichtum...", "&7Viel Spaß damit!"]
day_2: day_2:
name: "&6Goldene Äpfel" name: "&6Tag 2: Goldene Äpfel"
lore: ["&7Für den Fall, dass es mal eng wird..."] lore: ["&7Für den Fall, dass es mal eng wird..."]
day_3: day_3:
name: "&cSchwert des Helden" name: "&cTag 3: Schwert des Helden"
lore: ["&7Ein mächtiges Schwert, um Monster zu jagen."] lore: ["&7Ein mächtiges Schwert, um Monster zu jagen."]
day_4: day_4:
name: "&aFlügel der Freiheit" name: "&aTag 4: Flügel der Freiheit"
lore: ["&7Sei frei wie ein Adler!"] lore: ["&7Sei frei wie ein Adler!"]
day_5: day_5:
name: "&8Ein Hauch von Netherit" name: "&8Tag 5: Ein Hauch von Netherit"
lore: ["&7Sehr wertvoll und selten."] lore: ["&7Sehr wertvoll und selten."]
day_6: day_6:
name: "&6Göttlicher Apfel" name: "&6Tag 6: Göttlicher Apfel"
lore: ["&7Ein Geschenk der Götter... oder des Admins."] lore: ["&7Ein Geschenk der Götter... oder des Admins."]
day_7: day_7:
name: "&eBergbau-Meister" name: "&eTag 7: Bergbau-Meister"
lore: ["&7Für die größten Schätze der Welt."] lore: ["&7Für die größten Schätze der Welt."]
day_8: day_8:
name: "&dTotem des Unvergänglichen" name: "&dTag 8: Totem des Unvergänglichen"
lore: ["&7Ein kleines Extra-Leben."] lore: ["&7Ein kleines Extra-Leben."]
day_9: day_9:
name: "&5Stern des Nethers" name: "&5Tag 9: Stern des Nethers"
lore: ["&7Der Kern eines mächtigen Wesens."] lore: ["&7Der Kern eines mächtigen Wesens."]
day_10: day_10:
name: "&9Magische Truhe" name: "&9Tag 10: Magische Truhen"
lore: ["&7Hält all deine Schätze sicher."] lore: ["&7Hält all deine Schätze sicher."]
day_11: day_11:
name: "&3Stachel des Ozeans" name: "&3Tag 11: Stachel des Ozeans"
lore: ["&7Die Waffe der Wächter."] lore: ["&7Die Waffe der Wächter."]
day_12: day_12:
name: "&7Helm der Weisheit" name: "&7Tag 12: Helm der Weisheit"
lore: ["&7Schützt deinen Kopf."] lore: ["&7Schützt deinen Kopf."]
day_13: day_13:
name: "&fSattel für ein treues Tier" name: "&fTag 13: Sattel"
lore: ["&7Für dein nächstes Abenteuer zu Pferd."] lore: ["&7Für dein nächstes Abenteuer zu Pferd."]
day_14: day_14:
name: "&eNamensschilder" name: "&eTag 14: Namensschilder"
lore: ["&7Gib deinem Lieblings-Wolf einen Namen!"] lore: ["&7Gib deinem Lieblings-Wolf einen Namen!"]
day_15: day_15:
name: "&aSchallplatte - cat" name: "&aTag 15: Schallplatte - cat"
lore: ["&7Relaxende Musik für eine gemütliche Zeit."] lore: ["&7Relaxende Musik für eine gemütliche Zeit."]
day_16: day_16:
name: "&5Endkristall" name: "&5Tag 16: Endkristalle"
lore: ["&7Mächtig und gefährlich. Mit Vorsicht genießen!"] lore: ["&7Mächtig und gefährlich. Mit Vorsicht genießen!"]
day_17: day_17:
name: "&7Brustpanzer der Unzerstörbarkeit" name: "&7Tag 17: Brustpanzer der Unzerstörbarkeit"
lore: ["&7Bietet maximalen Schutz."] lore: ["&7Bietet maximalen Schutz."]
day_18: day_18:
name: "&bHerz des Ozeans" name: "&bTag 18: Herz des Ozeans"
lore: ["&7Das Zentrum einer Conduit-Struktur."] lore: ["&7Das Zentrum einer Conduit-Struktur."]
day_19: day_19:
name: "&fNautilusmuschel" name: "&fTag 19: Nautilusmuscheln"
lore: ["&7Ein seltenes Gut aus den Tiefen."] lore: ["&7Ein seltenes Gut aus den Tiefen."]
day_20: day_20:
name: "&8Kopf des Enderdrachen" name: "&8Tag 20: Kopf des Enderdrachen"
lore: ["&7Eine beeindruckende Trophäe."] lore: ["&7Eine beeindruckende Trophäe."]
day_21: day_21:
name: "&8Stiefel der Tiefe" name: "&8Tag 21: Stiefel der Tiefe"
lore: ["&7Lass dich nicht im Feuer verbrennen."] lore: ["&7Lass dich nicht im Feuer verbrennen."]
day_22: day_22:
name: "&dVerzauberungstisch" name: "&dTag 22: Verzauberungstisch"
lore: ["&7Kreiere deine eigenen magischen Items."] lore: ["&7Kreiere deine eigenen magischen Items."]
day_23: day_23:
name: "&bDie ultimative Hacke" name: "&bTag 23: Die ultimative Hacke"
lore: ["&7Nicht nur für den Ackerbau..."] lore: ["&7Nicht nur für den Ackerbau..."]
day_24: day_24:
name: "&cDas große Finale! - Feuerwerk" name: "&cTag 24: Das große Finale!"
lore: ["&7Frohe Weihnachten!", "&7Feiere mit einem riesigen Feuerwerk!"] lore: ["&7Frohe Weihnachten!", "&7Feiere mit einem riesigen Feuerwerk!"]

View File

@@ -1,15 +1,14 @@
# ============================================================================= # =============================================================================
# Adventskalender Plugin - English Language File # Adventskalender Plugin - English Language File (v2.0 - 2026)
# ============================================================================= # =============================================================================
# --- Prefix --- # --- Prefix ---
# This prefix will be shown before most chat messages. prefix: "&6[Advent] &r"
prefix: "&6[Adventskalender] &r"
# --- GUI Texts --- # --- GUI Texts ---
gui: gui:
# The title of the Advent Calendar inventory. # The title of the Advent Calendar inventory.
title: "&6✦ Advent Calendar 2024 ✦" title: "&6✦ Advent Calendar 2026 ✦"
# --- Messages to Players --- # --- Messages to Players ---
# Available placeholders: # Available placeholders:
@@ -18,90 +17,91 @@ gui:
messages: messages:
no_permission: "&cYou don't have permission for that." no_permission: "&cYou don't have permission for that."
only_player: "&cThis command can only be executed by a player." only_player: "&cThis command can only be executed by a player."
inventory_full: "&cYour inventory was full! The item was dropped on the ground." inventory_full: "&cYour inventory is full! Your gift was dropped at your feet."
reward_received: "&aYou have received your gift for day %day%!" reward_received: "&aCongratulations! You have received your gift for day &e%day%&a!"
day_not_available: "&cYou cannot open this gift (yet)." day_not_available: "&cThis door is still locked. You'll have to wait a bit longer!"
day_already_claimed: "&7You have already claimed your gift for this day." day_already_claimed: "&7You have already claimed this gift."
global_already_claimed: "&cThis gift has already been claimed by someone else today!"
# --- Admin Messages --- # --- Admin Messages ---
admin: admin:
reload_success: "&aConfiguration and language files have been reloaded." reload_success: "&aConfiguration and language files have been reloaded."
test_mode_on: "&e[Admin] &7Test mode set to day &6%day%&7."
test_mode_off: "&e[Admin] &7Test mode disabled. Using real-time date again."
player_not_found: "&cThe player '%player%' was not found." player_not_found: "&cThe player '%player%' was not found."
invalid_day: "&cInvalid day. Please choose a number between 1 and 24." invalid_day: "&cInvalid day. Please choose a number between 1 and 24."
open_success: "&aYou have opened day %day% for %player%."
# --- Names and Descriptions for Reward Items --- # --- Reward Texts in GUI ---
# The keys here (e.g., 'day_1') must match the 'keys' in the config.yml.
rewards: rewards:
day_1: day_1:
name: "&bFirst Diamonds!" name: "&bDay 1: First Diamonds!"
lore: ["&7A little taste of wealth...", "&7Have fun with it!"] lore: ["&7A little taste of wealth...", "&7Have fun with it!"]
day_2: day_2:
name: "&6Golden Apples" name: "&6Day 2: Golden Apples"
lore: ["&7In case things get tight..."] lore: ["&7In case things get tight..."]
day_3: day_3:
name: "&cSword of the Hero" name: "&cDay 3: Sword of the Hero"
lore: ["&7A mighty sword to hunt monsters."] lore: ["&7A mighty sword to hunt monsters."]
day_4: day_4:
name: "&aWings of Freedom" name: "&aDay 4: Wings of Freedom"
lore: ["&7Be free as an eagle!"] lore: ["&7Be free as an eagle!"]
day_5: day_5:
name: "&8A Touch of Netherite" name: "&8Day 5: A Touch of Netherite"
lore: ["&7Very valuable and rare."] lore: ["&7Very valuable and rare."]
day_6: day_6:
name: "&6Godly Apple" name: "&6Day 6: Godly Apple"
lore: ["&7A gift from the gods... or the admin."] lore: ["&7A gift from the gods... or the admin."]
day_7: day_7:
name: "&eMaster Pickaxe" name: "&eDay 7: Master Pickaxe"
lore: ["&7For the greatest treasures in the world."] lore: ["&7For the greatest treasures in the world."]
day_8: day_8:
name: "&dTotem of Undying" name: "&dDay 8: Totem of Undying"
lore: ["&7An extra life."] lore: ["&7An extra life."]
day_9: day_9:
name: "&5Nether Star" name: "&5Day 9: Nether Star"
lore: ["&7The core of a powerful being."] lore: ["&7The core of a powerful being."]
day_10: day_10:
name: "&9Magic Box" name: "&9Day 10: Magic Chests"
lore: ["&7Keeps all your treasures safe."] lore: ["&7Keeps all your treasures safe."]
day_11: day_11:
name: "&3Spear of the Ocean" name: "&3Day 11: Spear of the Ocean"
lore: ["&7The weapon of the guardians."] lore: ["&7The weapon of the guardians."]
day_12: day_12:
name: "&7Helmet of Wisdom" name: "&7Day 12: Helmet of Wisdom"
lore: ["&7Protects your head."] lore: ["&7Protects your head."]
day_13: day_13:
name: "&fSaddle for a Loyal Steed" name: "&fDay 13: Saddle"
lore: ["&7For your next equestrian adventure."] lore: ["&7For your next equestrian adventure."]
day_14: day_14:
name: "&eName Tags" name: "&eDay 14: Name Tags"
lore: ["&7Give your favorite wolf a name!"] lore: ["&7Give your favorite wolf a name!"]
day_15: day_15:
name: "&aMusic Disc - cat" name: "&aDay 15: Music Disc - cat"
lore: ["&7Relaxing music for a cozy time."] lore: ["&7Relaxing music for a cozy time."]
day_16: day_16:
name: "&5End Crystal" name: "&5Day 16: End Crystals"
lore: ["&7Powerful and dangerous. Handle with care!"] lore: ["&7Powerful and dangerous. Handle with care!"]
day_17: day_17:
name: "&7Chestplate of Durability" name: "&7Day 17: Chestplate of Durability"
lore: ["&7Offers maximum protection."] lore: ["&7Offers maximum protection."]
day_18: day_18:
name: "&bHeart of the Sea" name: "&bDay 18: Heart of the Sea"
lore: ["&7The center of a Conduit structure."] lore: ["&7The center of a Conduit structure."]
day_19: day_19:
name: "&fNautilus Shell" name: "&fDay 19: Nautilus Shells"
lore: ["&7A rare goodie from the depths."] lore: ["&7A rare goodie from the depths."]
day_20: day_20:
name: "&8Head of the Ender Dragon" name: "&8Day 20: Head of the Ender Dragon"
lore: ["&7An impressive trophy."] lore: ["&7An impressive trophy."]
day_21: day_21:
name: "&8Boots of the Deep" name: "&8Day 21: Boots of the Deep"
lore: ["&7Don't let yourself get burned in fire."] lore: ["&7Don't let yourself get burned in fire."]
day_22: day_22:
name: "&dEnchanting Table" name: "&dDay 22: Enchanting Table"
lore: ["&7Create your own magical items."] lore: ["&7Create your own magical items."]
day_23: day_23:
name: "&bThe Ultimate Hoe" name: "&bDay 23: The Ultimate Hoe"
lore: ["&7Not just for farming..."] lore: ["&7Not just for farming..."]
day_24: day_24:
name: "&cThe Grand Finale! - Fireworks" name: "&cDay 24: The Grand Finale!"
lore: ["&7Merry Christmas!", "&7Celebrate with a huge firework!"] lore: ["&7Merry Christmas!", "&7Celebrate with a huge firework!"]

View File

@@ -1,9 +1,9 @@
name: Adventskalender name: Adventskalender
version: '1.0.0' version: '1.0.1'
main: de.mviper.adventskalender.Adventskalender main: de.mviper.adventskalender.Adventskalender
api-version: 1.20 api-version: 1.20
author: M_Viper author: M_Viper
description: Ein konfigurierbarer Adventskalender für Minecraft. description: Ein moderner, performanter Adventskalender (2026 Edition).
commands: commands:
adventskalender: adventskalender: