# 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: ```java 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: ```java 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 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: ```java 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: ```java @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: ```java @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: ```java @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: ```java 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: ```java @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: ```java 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: ```java 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: ```java public final class Messages { private static volatile String currentLocale = "en_us"; private static volatile Map bundle = new HashMap<>(); private static volatile Map 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: ```java public final class LocaleLoader { public static Map 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: ```java 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: ```java 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 ```bash # Clone repository git clone cd PlayHours # Build the mod ./gradlew build # Output: build/libs/playhours-1.0.0.jar ``` ### Development Setup ```bash # 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: ```gradle 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: ```properties # 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](FEATURES.md)** - How features work - **[Configuration Guide](CONFIGURATION.md)** - Configuration details - **[Commands Reference](COMMANDS.md)** - Command implementation - **[Usage Examples](EXAMPLES.md)** - Real-world usage --- *For feature-specific details, see the [Features Overview](FEATURES.md).*