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:
485
docs/TECHNICAL.md
Normal file
485
docs/TECHNICAL.md
Normal file
@@ -0,0 +1,485 @@
|
||||
# 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<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:
|
||||
|
||||
```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<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:
|
||||
|
||||
```java
|
||||
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:
|
||||
|
||||
```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 <repository-url>
|
||||
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).*
|
||||
Reference in New Issue
Block a user