- 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.
486 lines
13 KiB
Markdown
486 lines
13 KiB
Markdown
# 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).*
|