Compare commits

1 Commits
master ... dev

Author SHA1 Message Date
d4680b80d6 Inital Commit 2025-10-21 17:41:51 +02:00
11 changed files with 557 additions and 0 deletions

113
.gitignore vendored Normal file
View File

@@ -0,0 +1,113 @@
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# Common working directory
run/

68
pom.xml Normal file
View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.user404_</groupId>
<artifactId>ServerCountdown</artifactId>
<version>1.0.0-dev</version>
<packaging>jar</packaging>
<name>ServerCountdown</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<defaultGoal>clean package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,133 @@
package com.user404_.serverCountdown;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
public class CountdownManager {
private boolean isPreparing = false;
private boolean isRunning = false;
private Location teleportLocation;
private Map<UUID, Location> previousLocations = new HashMap<>();
private BukkitRunnable teleportTask;
private BukkitRunnable countdownTask;
public void startPreparation(Location loc) {
isPreparing = true;
teleportLocation = loc;
for (Player p : Bukkit.getOnlinePlayers()) {
applyEffects(p);
previousLocations.put(p.getUniqueId(), p.getLocation().clone());
}
teleportTask = new BukkitRunnable() {
@Override
public void run() {
if (!isPreparing && !isRunning) {
cancel();
return;
}
for (Player p : Bukkit.getOnlinePlayers()) {
if (teleportLocation != null) {
p.teleport(teleportLocation);
}
}
}
};
teleportTask.runTaskTimer(ServerCountdown.getInstance(), 0, 200); // 10 Sekunden = 200 Ticks
}
public void startCountdown(int seconds) {
if (!isPreparing) {
return;
}
isPreparing = false;
isRunning = true;
if (teleportTask != null) {
teleportTask.cancel();
}
countdownTask = new BukkitRunnable() {
int timeLeft = seconds;
@Override
public void run() {
if (timeLeft <= 0) {
endCountdown();
cancel();
return;
}
// Send title to all players
for (Player p : Bukkit.getOnlinePlayers()) {
String message = LanguageManager.getMessage(p, "countdown-title");
message = message.replace("{0}", String.valueOf(timeLeft));
p.sendTitle(message, "", 0, 40, 10);
}
timeLeft--;
}
};
countdownTask.runTaskTimer(ServerCountdown.getInstance(), 0, 20); // 1 Sekunde = 20 Ticks
}
private void endCountdown() {
isRunning = false;
for (Player p : Bukkit.getOnlinePlayers()) {
removeEffects(p);
Location original = previousLocations.get(p.getUniqueId());
if (original != null) {
p.teleport(original);
}
}
previousLocations.clear();
// Remove effects from all players, even if they're offline
for (UUID playerId : previousLocations.keySet()) {
Player player = Bukkit.getPlayer(playerId);
if (player != null && player.isOnline()) {
removeEffects(player);
}
}
}
public void applyEffects(Player p) {
p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Integer.MAX_VALUE, 255, false, false, false));
p.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, Integer.MAX_VALUE, 255, false, false, false));
p.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, Integer.MAX_VALUE, 255, false, false, false));
}
public void removeEffects(Player p) {
p.removePotionEffect(PotionEffectType.BLINDNESS);
p.removePotionEffect(PotionEffectType.SLOWNESS);
p.removePotionEffect(PotionEffectType.MINING_FATIGUE);
}
public Location getTeleportLocation() {
return teleportLocation;
}
public boolean isPreparing() {
return isPreparing;
}
public boolean isRunning() {
return isRunning;
}
public void cleanup() {
if (teleportTask != null) {
teleportTask.cancel();
}
if (countdownTask != null) {
countdownTask.cancel();
}
}
}

View File

@@ -0,0 +1,62 @@
package com.user404_.serverCountdown;
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.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class LanguageManager {
private static final String LANG_FOLDER = "texts";
public static void copyDefaultLanguages() {
File dataFolder = ServerCountdown.getInstance().getDataFolder();
File langFolder = new File(dataFolder, LANG_FOLDER);
if (!langFolder.exists()) {
langFolder.mkdirs();
}
String[] languages = {"en.yml", "de.yml"};
for (String lang : languages) {
File file = new File(langFolder, lang);
if (!file.exists()) {
try (InputStream in = ServerCountdown.class.getClassLoader().getResourceAsStream(lang)) {
if (in != null) {
Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
ServerCountdown.getInstance().getLogger().info("Created language file: " + lang);
} else {
ServerCountdown.getInstance().getLogger().warning("Could not find " + lang + " in resources!");
}
} catch (IOException e) {
ServerCountdown.getInstance().getLogger().severe("Failed to create language file: " + lang);
e.printStackTrace();
}
}
}
}
public static String getMessage(Player p, String key) {
String language = "en"; // Default to English
if (p.getLocale() != null && p.getLocale().toLowerCase().contains("de")) {
language = "de";
}
File file = new File(ServerCountdown.getInstance().getDataFolder(), LANG_FOLDER + "/" + language + ".yml");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
String message = config.getString(key);
if (message == null) {
// Fallback to English if message not found
File enFile = new File(ServerCountdown.getInstance().getDataFolder(), LANG_FOLDER + "/en.yml");
FileConfiguration enConfig = YamlConfiguration.loadConfiguration(enFile);
message = enConfig.getString(key, "Message not found: " + key);
}
return message;
}
}

View File

@@ -0,0 +1,25 @@
package com.user404_.serverCountdown;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
public class PlayerListener implements Listener {
private final CountdownManager countdownManager;
public PlayerListener(CountdownManager countdownManager) {
this.countdownManager = countdownManager;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
if (countdownManager.isPreparing() || countdownManager.isRunning()) {
countdownManager.applyEffects(p);
if (countdownManager.isPreparing() && countdownManager.getTeleportLocation() != null) {
p.teleport(countdownManager.getTeleportLocation());
}
}
}
}

View File

@@ -0,0 +1,38 @@
package com.user404_.serverCountdown;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class PrepCDCommand implements CommandExecutor {
private final CountdownManager countdownManager;
public PrepCDCommand(CountdownManager countdownManager) {
this.countdownManager = countdownManager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("§cOnly players can use this command!");
return true;
}
Player player = (Player) sender;
if (!player.hasPermission("servercountdown.cd.prep")) {
player.sendMessage("§cYou don't have permission to use this command!");
return true;
}
if (countdownManager.isPreparing() || countdownManager.isRunning()) {
player.sendMessage("§cCountdown is already active!");
return true;
}
countdownManager.startPreparation(player.getLocation());
player.sendMessage("§aCountdown preparation started!");
return true;
}
}

View File

@@ -0,0 +1,40 @@
package com.user404_.serverCountdown;
import org.bukkit.plugin.java.JavaPlugin;
public class ServerCountdown extends JavaPlugin {
private static ServerCountdown instance;
private CountdownManager countdownManager;
@Override
public void onEnable() {
instance = this;
countdownManager = new CountdownManager();
getCommand("prepcd").setExecutor(new PrepCDCommand(countdownManager));
getCommand("startcd").setExecutor(new StartCDCommand(countdownManager));
getServer().getPluginManager().registerEvents(new PlayerListener(countdownManager), this);
// Create language files
LanguageManager.copyDefaultLanguages();
getLogger().info("ServerCountdown enabled successfully!");
}
@Override
public void onDisable() {
if (countdownManager != null) {
countdownManager.cleanup();
}
getLogger().info("ServerCountdown disabled!");
}
public static ServerCountdown getInstance() {
return instance;
}
public CountdownManager getCountdownManager() {
return countdownManager;
}
}

View File

@@ -0,0 +1,54 @@
package com.user404_.serverCountdown;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class StartCDCommand implements CommandExecutor {
private final CountdownManager countdownManager;
public StartCDCommand(CountdownManager countdownManager) {
this.countdownManager = countdownManager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("§cOnly players can use this command!");
return true;
}
Player player = (Player) sender;
if (!player.hasPermission("servercountdown.cd.start")) {
player.sendMessage("§cYou don't have permission to use this command!");
return true;
}
if (args.length != 1) {
player.sendMessage("§cUsage: /startcd <seconds>");
return false;
}
if (!countdownManager.isPreparing()) {
player.sendMessage("§cYou must prepare the countdown first with /prepcd!");
return true;
}
try {
int time = Integer.parseInt(args[0]);
if (time < 1 || time > 100) {
player.sendMessage("§cTime must be between 1 and 100 seconds!");
return false;
}
countdownManager.startCountdown(time);
player.sendMessage("§aCountdown started for " + time + " seconds!");
} catch (NumberFormatException e) {
player.sendMessage("§cPlease enter a valid number!");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,3 @@
preparation-started: "Countdown-Vorbereitung gestartet!"
countdown-started: "Countdown gestartet!"
countdown-title: "§c{0}"

View File

@@ -0,0 +1,3 @@
preparation-started: "Countdown preparation started!"
countdown-started: "Countdown started!"
countdown-title: "§c{0}"

View File

@@ -0,0 +1,18 @@
name: ServerCountdown
version: 1.0.0-dev
website: https://github.com/deutschich/ServerCountdown/
main: com.user404_.serverCountdown.ServerCountdown
api-version: 1.21
commands:
prepcd:
description: Prepares countdown
permission: servercountdown.cd.prep
startcd:
description: Starts countdown
usage: /<command> <time>
permission: servercountdown.cd.start
permissions:
servercountdown.cd.prep:
default: op
servercountdown.cd.start:
default: op