From 3b43b594f455de8b3df090114e0fd4f9c163ad68 Mon Sep 17 00:00:00 2001 From: besiece Date: Sun, 8 Nov 2026 18:42:32 +0100 Subject: [PATCH] Add DimensionScheduler and DimensionScheduleCommand for managing dimension access timings --- .../DimensionScheduleCommand.java | 39 ++++++++++ .../DimensionScheduler.java | 75 +++++++++++++++++++ .../src/main/resources/dimension-schedule.yml | 5 ++ 3 files changed, 119 insertions(+) create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduleCommand.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduler.java create mode 100644 common/src/main/resources/dimension-schedule.yml diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduleCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduleCommand.java new file mode 100644 index 0000000..778d969 --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduleCommand.java @@ -0,0 +1,39 @@ +package eu.mhsl.craftattack.spawn.common.appliances.tooling.dimensionScheduler; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.List; + +class DimensionScheduleCommand extends ApplianceCommand { + public DimensionScheduleCommand() { + super("dimensionSchedule"); + } + + @Override + protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if(!sender.isOp()) throw new Error("Dieser Befehl ist nur für die Konsole und OPs verfügbar."); + if(args.length != 1 || !args[0].equalsIgnoreCase("reload")) { + throw new Error("Verwendung: /dimensionSchedule reload"); + } + + try { + this.getAppliance().reload(); + } catch(IOException | InvalidConfigurationException | IllegalArgumentException e) { + throw new Error("dimension-schedule.yml konnte nicht neu eingelesen werden: " + e.getMessage()); + } + sender.sendMessage(Component.text("dimension-schedule.yml wurde neu eingelesen.", NamedTextColor.GREEN)); + } + + @Override + public @NotNull List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if(!sender.isOp() || args.length != 1) return List.of(); + return this.tabCompleteReducer(List.of("reload"), args); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduler.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduler.java new file mode 100644 index 0000000..a02536e --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/dimensionScheduler/DimensionScheduler.java @@ -0,0 +1,75 @@ +package eu.mhsl.craftattack.spawn.common.appliances.tooling.dimensionScheduler; + +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.format.DateTimeParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; + +/** Reads named times only; applying them to dimension access is handled separately. */ +public class DimensionScheduler extends Appliance { + private static final String FILE_NAME = "dimension-schedule.yml"; + private final File scheduleFile = new File(Main.instance().getDataFolder(), FILE_NAME); + private Map times = Map.of(); + + @Override + public void onEnable() { + try { + if(!this.scheduleFile.exists()) Main.instance().saveResource(FILE_NAME, false); + this.reload(); + } catch(IOException | InvalidConfigurationException | IllegalArgumentException e) { + Main.logger().log(Level.WARNING, "Could not load " + FILE_NAME + "; keeping previous times", e); + } + } + + /** Validates all entries before replacing the current times. Never creates or writes the file. */ + public void reload() throws IOException, InvalidConfigurationException { + YamlConfiguration configuration = new YamlConfiguration(); + configuration.load(this.scheduleFile); + + Map loadedTimes = new HashMap<>(); + for(Map.Entry entry : configuration.getValues(false).entrySet()) { + readTime(entry.getKey(), entry.getValue()).ifPresent(time -> loadedTimes.put(entry.getKey(), time)); + } + this.times = Map.copyOf(loadedTimes); + } + + private static Optional readTime(String key, Object configured) { + if(configured == null) return Optional.empty(); + if(!(configured instanceof String value)) { + throw new IllegalArgumentException(key + " muss null oder ein Datum in Anführungszeichen mit UTC-Offset sein."); + } + if(value.isBlank()) return Optional.empty(); + try { + return Optional.of(OffsetDateTime.parse(value.trim()).toInstant()); + } catch(DateTimeParseException e) { + throw new IllegalArgumentException("Ungültiger Zeitpunkt für " + key + ". Beispiel: \"2026-11-15T18:00:00+01:00\"", e); + } + } + + public Optional getTime(String name) { + return Optional.ofNullable(this.times.get(name)); + } + + /** Immutable snapshot of all configured, non-empty times. */ + public Map getTimes() { + return this.times; + } + + @Override + protected @NotNull List> commands() { + return List.of(new DimensionScheduleCommand()); + } +} diff --git a/common/src/main/resources/dimension-schedule.yml b/common/src/main/resources/dimension-schedule.yml new file mode 100644 index 0000000..5595eb3 --- /dev/null +++ b/common/src/main/resources/dimension-schedule.yml @@ -0,0 +1,5 @@ +# Datum mit UTC-Offset, z. B. "2026-11-15T18:00:00+01:00" +# null bedeutet: kein Termin +# Weitere Einträge mit frei wählbaren Namen können ergänzt werden. +end-open-at: null +outer-end-open-at: null