feat(docs): complete PlayHours mod implementation with comprehensive documentation

- Add complete PlayHours mod source code with all features:
  * Schedule enforcement with per-day schedules and midnight-spanning support
  * Login control with configurable thresholds and exemptions
  * Warnings and auto-kick system with countdown functionality
  * Force modes (NORMAL/FORCE_OPEN/FORCE_CLOSED) for maintenance
  * Whitelist/blacklist system for player access control
  * Date exceptions for holidays and special events
  * Multi-language support (English/French) with smart time formatting
  * LuckPerms integration with vanilla ops fallback
  * Dynamic MOTD system with real-time schedule display
  * Comprehensive command system with permission integration
  * TOML configuration with hot-reload support

- Add comprehensive documentation suite:
  * Installation guide with step-by-step setup instructions
  * Complete configuration reference with all options
  * Commands reference with usage examples
  * Features overview with detailed explanations
  * MOTD system configuration and customization guide
  * Permissions system documentation with LuckPerms integration
  * Technical details covering architecture and limitations
  * Usage examples with real-world scenarios
  * Changelog with version history

- Add resource files:
  * Language files (en_us.json, fr_fr.json) with localized messages
  * Mod metadata (mods.toml) with proper Forge configuration
  * Resource pack metadata (pack.mcmeta)

- Update build configuration:
  * Gradle build system with proper dependencies
  * Project properties and version management
  * Development environment setup

- Restructure documentation:
  * Replace old README.txt with new comprehensive README.md
  * Create modular documentation structure in docs/ directory
  * Add cross-references and navigation between documents
  * Include quick start guide and common use cases

This commit represents the complete v1.0.0 release of PlayHours, a production-ready server operation hours enforcement mod for Minecraft Forge 1.20.1.
This commit is contained in:
Mr¤KayJayDee
2025-10-23 23:28:20 +02:00
parent 58919ced40
commit c0fd2a2787
63 changed files with 6974 additions and 265 deletions

View File

@@ -0,0 +1,93 @@
package com.mrkayjaydee.playhours.events;
import com.mrkayjaydee.playhours.core.ScheduleService;
import com.mrkayjaydee.playhours.config.*;
import com.mrkayjaydee.playhours.text.Messages;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.time.ZonedDateTime;
import java.util.*;
/**
* Handles periodic server tick processing for warnings and auto-kick at closing time.
* Runs checks every second (20 ticks) to broadcast warnings and enforce closing times.
*/
@Mod.EventBusSubscriber
public class TickScheduler {
private static final int TICKS_PER_SECOND = 20;
private static int tickCounter = 0;
private static final Set<Integer> sentCountdowns = new HashSet<>();
/**
* Server tick event handler that checks for closing warnings and kicks players at closing time.
*/
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
tickCounter++;
if (tickCounter % TICKS_PER_SECOND != 0) return; // once per second
var server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer();
if (server == null) return;
ScheduleService sched = ScheduleService.get();
if (!ConfigEventHandler.isReady()) {
// Config not ready yet; avoid accessing values
com.mrkayjaydee.playhours.PlayHoursMod.LOGGER.debug("Tick skipped: config not ready");
return;
}
ZonedDateTime now = ZonedDateTime.now(sched.getZoneId());
boolean open = sched.isOpen(now);
if (open) {
Optional<ZonedDateTime> nextClose = sched.nextClose(now);
if (nextClose.isPresent()) {
// Broadcast warnings
WarningBroadcaster.broadcastWarnings(server, sched, now, nextClose.get());
// Handle countdown messages
handleCountdown(server, now, nextClose.get());
// Kick at close - use reliable time comparison
if (!now.isBefore(nextClose.get())) {
List<ServerPlayer> players = new ArrayList<>(server.getPlayerList().getPlayers());
PlayerKickHandler.kickPlayersAtClose(players, sched, nextClose.get());
WarningBroadcaster.clearWarnings();
sentCountdowns.clear();
}
} else {
WarningBroadcaster.clearWarnings();
sentCountdowns.clear();
}
} else {
WarningBroadcaster.clearWarnings();
sentCountdowns.clear();
}
}
/**
* Handles countdown messages before closing.
* Sends messages every second for the configured number of seconds before closing.
*/
private static void handleCountdown(net.minecraft.server.MinecraftServer server, ZonedDateTime now, ZonedDateTime nextClose) {
int countdownSeconds = GeneralConfig.COUNTDOWN_SECONDS.get();
if (countdownSeconds <= 0) return;
long secondsUntilClose = java.time.Duration.between(now, nextClose).getSeconds();
if (secondsUntilClose <= countdownSeconds && secondsUntilClose > 0) {
int seconds = (int) secondsUntilClose;
// Only send if we haven't sent this countdown yet
if (!sentCountdowns.contains(seconds)) {
sentCountdowns.add(seconds);
server.getPlayerList().broadcastSystemMessage(Messages.countdown(seconds), false);
}
}
}
}