Files
PlayHours/docs/TECHNICAL.md
Mr¤KayJayDee c0fd2a2787 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.
2025-10-23 23:28:20 +02:00

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

For feature-specific details, see the Features Overview.