- 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.
13 KiB
Technical Details
This document covers the technical architecture, implementation details, and limitations of the PlayHours mod.
🏗️ Architecture Overview
Core Components
PlayHours is built with a modular architecture consisting of several key components:
- Core Logic - Schedule calculation and enforcement
- Event Handlers - Server tick and login event processing
- Configuration - TOML-based configuration management
- Commands - Brigadier-based command system
- Permissions - LuckPerms integration with ops fallback
- Text System - Localization and message formatting
- MOTD System - Dynamic server list updates
Class Structure
com.mrkayjaydee.playhours/
├── PlayHoursMod.java # Main mod class
├── command/ # Command system
│ ├── HoursCommand.java # Main command registration
│ ├── CommandBuilder.java # Command utilities
│ └── [Command Builders] # Specialized command builders
├── config/ # Configuration system
│ ├── ServerConfig.java # Main config class
│ ├── GeneralConfig.java # General settings
│ ├── DaysConfig.java # Day-specific schedules
│ ├── ExceptionsConfig.java # Date exceptions
│ ├── ListsConfig.java # Whitelist/blacklist
│ ├── MessagesConfig.java # Message customization
│ └── MOTDConfig.java # MOTD settings
├── core/ # Core logic
│ ├── ScheduleService.java # Main schedule service
│ ├── ScheduleCalculator.java # Schedule calculations
│ ├── ExceptionHandler.java # Exception handling
│ ├── PlayerAccessChecker.java # Access control
│ ├── TimeRange.java # Time range representation
│ └── ForceMode.java # Force mode enum
├── events/ # Event handlers
│ ├── LoginGuard.java # Login event handling
│ ├── TickScheduler.java # Server tick processing
│ ├── MOTDHandler.java # MOTD updates
│ └── [Event Handlers] # Other event handlers
├── permissions/ # Permission system
│ ├── PermissionChecker.java # Main permission checker
│ ├── LuckPermsIntegration.java # LuckPerms integration
│ └── PermissionConstants.java # Permission constants
└── text/ # Text system
├── Messages.java # Message management
├── LocaleLoader.java # Locale loading
└── [Text Handlers] # Text processing
⚙️ Core Logic
Schedule Service
The ScheduleService class is the central coordinator for all schedule-related operations:
public final class ScheduleService {
private final PlayerAccessChecker accessChecker;
private final ScheduleCalculator scheduleCalculator;
private final ExceptionHandler exceptionHandler;
// Main schedule check
public boolean isOpen(ZonedDateTime now) {
// 1. Check force mode
// 2. Check exceptions
// 3. Check daily schedule
}
// Player-specific access check
public boolean isOpenForName(String name, boolean isExempt) {
// 1. Check player access (lists, force mode)
// 2. Check schedule if not exempt
}
}
Schedule Calculation
The ScheduleCalculator handles time-based schedule calculations:
public final class ScheduleCalculator {
// Check if schedule is open at specific time
public boolean isScheduleOpen(ZonedDateTime now) {
// 1. Get current day of week
// 2. Get periods for that day
// 3. Check if current time falls within any period
// 4. Handle midnight-spanning periods
}
// Calculate next open/close times
public Optional<ZonedDateTime> nextClose(ZonedDateTime now) {
// Search through upcoming periods
// Handle midnight-spanning periods
// Return next closing time
}
}
Time Range Handling
The TimeRange class represents time periods with support for midnight spanning:
public final class TimeRange {
private final LocalTime start;
private final LocalTime end;
private final boolean spansMidnight;
// Check if time falls within range
public boolean contains(LocalTime time) {
if (spansMidnight) {
return time.isAfter(start) || time.isBefore(end);
} else {
return time.isAfter(start) && time.isBefore(end);
}
}
}
🔄 Event System
Login Guard
The LoginGuard class handles player login events:
@Mod.EventBusSubscriber
public class LoginGuard {
@SubscribeEvent
public static void onLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
// 1. Check if player is allowed by schedule/lists
// 2. Check if within closing threshold
// 3. Disconnect player if denied
}
}
Tick Scheduler
The TickScheduler class handles server tick events for warnings and kicks:
@Mod.EventBusSubscriber
public class TickScheduler {
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
// 1. Check if server is open
// 2. Broadcast warnings if closing soon
// 3. Handle countdown messages
// 4. Kick players at closing time
}
}
MOTD Handler
The MOTDHandler class manages dynamic MOTD updates:
@Mod.EventBusSubscriber
public class MOTDHandler {
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
// 1. Check if MOTD is enabled
// 2. Update MOTD periodically
// 3. Build MOTD content
// 4. Apply to server
}
}
🔧 Configuration System
Forge Config Integration
PlayHours uses Forge's configuration system with TOML format:
public final class ServerConfig {
public static final ForgeConfigSpec SPEC;
static {
ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
// Initialize all config sections
GeneralConfig.init(BUILDER);
DefaultsConfig.init(BUILDER);
DaysConfig.init(BUILDER);
ExceptionsConfig.init(BUILDER);
ListsConfig.init(BUILDER);
MessagesConfig.init(BUILDER);
MOTDConfig.init(BUILDER);
SPEC = BUILDER.build();
}
}
Configuration Reload
The ConfigEventHandler handles configuration changes:
@Mod.EventBusSubscriber
public class ConfigEventHandler {
@SubscribeEvent
public static void onConfigLoad(ModConfigEvent event) {
// 1. Validate configuration
// 2. Update schedule service
// 3. Reload messages
// 4. Update MOTD settings
}
}
🔑 Permission System
Permission Checker
The PermissionChecker class handles permission validation:
public final class PermissionChecker {
public static boolean hasAdmin(ServerPlayer player) {
return hasPermission(player, PermissionConstants.ADMIN, PermissionConstants.ADMIN_FALLBACK_LEVEL);
}
private static boolean hasPermission(ServerPlayer player, String permission, int fallbackLevel) {
// 1. Try LuckPerms if available
// 2. Fall back to vanilla ops
// 3. Handle timeouts gracefully
}
}
LuckPerms Integration
The LuckPermsIntegration class handles LuckPerms-specific operations:
public final class LuckPermsIntegration {
private static LuckPerms luckPerms;
static {
try {
luckPerms = LuckPermsProvider.get();
} catch (Throwable ignored) {
luckPerms = null;
}
}
public static boolean hasPermission(ServerPlayer player, String permission) {
if (!isAvailable()) return false;
var user = luckPerms.getUserManager().getUser(player.getUUID());
if (user == null) return false;
var data = user.getCachedData().getPermissionData();
return data.checkPermission(permission).asBoolean();
}
}
💬 Text System
Message Management
The Messages class handles localized message loading and formatting:
public final class Messages {
private static volatile String currentLocale = "en_us";
private static volatile Map<String, String> bundle = new HashMap<>();
private static volatile Map<String, String> overrides = new HashMap<>();
public static Component accessDenied(String openDay, String openTime) {
// 1. Get message template
// 2. Replace placeholders
// 3. Return formatted message
}
}
Locale Loading
The LocaleLoader class handles language file loading:
public final class LocaleLoader {
public static Map<String, String> loadLocale(String locale) {
// 1. Load language file from resources
// 2. Parse JSON content
// 3. Return message mappings
}
}
📢 MOTD System
MOTD Builder
The MOTDBuilder class constructs MOTD content:
public final class MOTDBuilder {
public static Component build(ScheduleService scheduleService, ZonedDateTime now) {
// 1. Check current status
// 2. Build status line
// 3. Add next open/close times
// 4. Add countdown if applicable
// 5. Apply colors and formatting
}
}
MOTD Validator
The MOTDValidator class ensures MOTD content fits Minecraft limits:
public final class MOTDValidator {
public static Component validateAndTruncate(Component motd) {
// 1. Check line count (max 2 lines)
// 2. Check character count (max 59 per line)
// 3. Truncate if necessary
// 4. Return validated MOTD
}
}
🚨 Known Limitations
Early Login Denial
Issue: Forge 1.20.1 lacks a true pre-join network event.
Impact: Players briefly "join" before being disconnected.
Workaround: Uses PlayerLoggedInEvent with immediate disconnection.
Future: Could be improved with newer Forge versions.
Deprecation Warning
Issue: FMLJavaModLoadingContext.get() is deprecated.
Impact: Warning in server logs.
Reason: Required for 1.20.1 compatibility.
Future: Will be updated for newer Forge versions.
MOTD Caching
Issue: Some server list clients may cache MOTD content.
Impact: MOTD updates may not be immediately visible.
Workaround: MOTD updates every 60 seconds by default.
Future: Could be improved with more frequent updates.
🔧 Performance Considerations
Schedule Caching
- Cached calculations - Schedule data is cached for performance
- Efficient lookups - Time-based lookups are optimized
- Minimal memory usage - Only essential data is cached
Event Processing
- Tick optimization - Events are processed efficiently
- Minimal overhead - Only necessary checks are performed
- Resource management - Proper cleanup of resources
Permission Checking
- Cached results - Permission results are cached
- Timeout protection - Prevents blocking on permission checks
- Fallback handling - Graceful degradation on errors
🛠️ Building from Source
Requirements
- Java: 17 or 23
- Gradle: 8.8+ (included via wrapper)
- Forge: 47.4.10+
Build Process
# Clone repository
git clone <repository-url>
cd PlayHours
# Build the mod
./gradlew build
# Output: build/libs/playhours-1.0.0.jar
Development Setup
# Setup development environment
./gradlew genEclipseRuns # For Eclipse
./gradlew genIntellijRuns # For IntelliJ IDEA
# Run development server
./gradlew runServer
Build Configuration
The build.gradle file contains the build configuration:
plugins {
id 'net.minecraftforge.gradle' version '5.1.+'
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
}
minecraft {
version = '1.20.1'
mappings channel: 'official', version: '1.20.1'
runs {
server {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
}
}
}
🔍 Debugging
Log Levels
PlayHours uses different log levels for different types of information:
- INFO - General information and status
- WARN - Warnings and fallback behavior
- ERROR - Errors and exceptions
- DEBUG - Detailed debugging information
Debug Configuration
Enable debug logging by setting the log level:
# In server.properties or log4j2.xml
logger.playhours.level=DEBUG
Common Debug Messages
[INFO] PlayHours loading... (modId=playhours)
[INFO] PlayHours common setup initialized
[WARN] PlayHours: LuckPerms not found, using ops fallback
[DEBUG] PlayHours: Config not ready yet
[ERROR] PlayHours: Permission check timeout for user PlayerName
📚 Related Documentation
- Features Overview - How features work
- Configuration Guide - Configuration details
- Commands Reference - Command implementation
- Usage Examples - Real-world usage
For feature-specific details, see the Features Overview.