- 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.
115 lines
5.6 KiB
Java
115 lines
5.6 KiB
Java
package com.mrkayjaydee.playhours.command;
|
|
|
|
import com.mojang.brigadier.CommandDispatcher;
|
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
import com.mrkayjaydee.playhours.core.ScheduleService;
|
|
import com.mrkayjaydee.playhours.core.TimeRange;
|
|
import com.mrkayjaydee.playhours.core.ForceModeFormatter;
|
|
import com.mrkayjaydee.playhours.text.Messages;
|
|
import com.mrkayjaydee.playhours.config.*;
|
|
import net.minecraft.commands.CommandSourceStack;
|
|
import net.minecraft.commands.Commands;
|
|
|
|
import java.time.ZonedDateTime;
|
|
import java.time.format.TextStyle;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Main entry point for /hours command registration.
|
|
* Delegates to specialized builders for different command groups.
|
|
*/
|
|
public final class HoursCommand {
|
|
private HoursCommand() {}
|
|
|
|
/**
|
|
* Registers the /hours command tree with the Minecraft command dispatcher.
|
|
* @param d the command dispatcher
|
|
*/
|
|
public static void register(CommandDispatcher<CommandSourceStack> d) {
|
|
d.register(Commands.literal("hours")
|
|
.requires(src -> src.hasPermission(0))
|
|
.then(registerStatusCommand())
|
|
.then(registerForceCommand())
|
|
.then(registerReloadCommand())
|
|
.then(SetCommandBuilder.build())
|
|
.then(registerExceptionsCommand())
|
|
.then(ListsCommandBuilder.build())
|
|
.then(MOTDCommandBuilder.build())
|
|
.then(MessagesCommandBuilder.build())
|
|
);
|
|
}
|
|
|
|
private static LiteralArgumentBuilder<CommandSourceStack> registerStatusCommand() {
|
|
return CommandBuilder.viewLiteral("status")
|
|
.executes(ctx -> {
|
|
if (!ConfigEventHandler.isReady()) {
|
|
ctx.getSource().sendFailure(Messages.configNotReady());
|
|
return 0;
|
|
}
|
|
ScheduleService s = ScheduleService.get();
|
|
ZonedDateTime now = ZonedDateTime.now(s.getZoneId());
|
|
boolean open = s.isOpen(now);
|
|
String nextClose = s.nextClose(now).map(z -> TimeRange.formatTime(z.toLocalTime(), Messages.getJavaLocale())).orElse("-");
|
|
var no = s.nextOpen(now);
|
|
String day = no.map(z -> z.getDayOfWeek().getDisplayName(TextStyle.FULL, Messages.getJavaLocale())).orElse("-");
|
|
String time = no.map(z -> TimeRange.formatTime(z.toLocalTime(), Messages.getJavaLocale())).orElse("-");
|
|
String modeDisplay = ForceModeFormatter.format(s.getForceMode());
|
|
ctx.getSource().sendSuccess(() -> Messages.statusLine(modeDisplay, open, nextClose, day, time), false);
|
|
return 1;
|
|
});
|
|
}
|
|
|
|
private static LiteralArgumentBuilder<CommandSourceStack> registerForceCommand() {
|
|
return CommandBuilder.adminLiteral("force")
|
|
.then(Commands.literal("normal").executes(ctx -> setForce("NORMAL", ctx.getSource())))
|
|
.then(Commands.literal("open").executes(ctx -> setForce("FORCE_OPEN", ctx.getSource())))
|
|
.then(Commands.literal("close").executes(ctx -> setForce("FORCE_CLOSED", ctx.getSource())));
|
|
}
|
|
|
|
private static LiteralArgumentBuilder<CommandSourceStack> registerReloadCommand() {
|
|
return CommandBuilder.adminLiteral("reload")
|
|
.executes(ctx -> {
|
|
com.mrkayjaydee.playhours.PlayHoursMod.LOGGER.info("/hours reload invoked by {}", ctx.getSource().getTextName());
|
|
ConfigEventHandler.reloadFromDisk();
|
|
ctx.getSource().sendSuccess(() -> Messages.configReloaded(), true);
|
|
return 1;
|
|
});
|
|
}
|
|
|
|
private static LiteralArgumentBuilder<CommandSourceStack> registerExceptionsCommand() {
|
|
return CommandBuilder.adminLiteral("exceptions")
|
|
.then(Commands.literal("add-open").then(Commands.argument("spec", StringArgumentType.greedyString()).executes(ctx -> {
|
|
String spec = StringArgumentType.getString(ctx, "spec");
|
|
List<String> list = new ArrayList<>(ExceptionsConfig.OPEN_DATES.get());
|
|
list.add(spec);
|
|
ExceptionsConfig.OPEN_DATES.set(list);
|
|
CommandBuilder.saveAndRebuild(ctx.getSource());
|
|
return 1;
|
|
})))
|
|
.then(Commands.literal("add-closed").then(Commands.argument("spec", StringArgumentType.greedyString()).executes(ctx -> {
|
|
String spec = StringArgumentType.getString(ctx, "spec");
|
|
List<String> list = new ArrayList<>(ExceptionsConfig.CLOSED_DATES.get());
|
|
list.add(spec);
|
|
ExceptionsConfig.CLOSED_DATES.set(list);
|
|
CommandBuilder.saveAndRebuild(ctx.getSource());
|
|
return 1;
|
|
})))
|
|
.then(Commands.literal("clear").executes(ctx -> {
|
|
ExceptionsConfig.OPEN_DATES.set(new ArrayList<>());
|
|
ExceptionsConfig.CLOSED_DATES.set(new ArrayList<>());
|
|
CommandBuilder.saveAndRebuild(ctx.getSource());
|
|
return 1;
|
|
}));
|
|
}
|
|
|
|
private static int setForce(String mode, CommandSourceStack src) {
|
|
GeneralConfig.FORCE_MODE.set(mode);
|
|
CommandBuilder.saveAndRebuild(src);
|
|
if ("FORCE_OPEN".equals(mode)) src.sendSuccess(() -> Messages.forceOpen(), true);
|
|
if ("FORCE_CLOSED".equals(mode)) src.sendSuccess(() -> Messages.forceClosed(), true);
|
|
return 1;
|
|
}
|
|
}
|
|
|