feat(sleep): add autosleep subcommands and manager#1
Conversation
Add "once", "auto" and "stop" subcommands to the sleep command registration so users can explicitly sleep once, enable automatic sleeping, or disable it. Wire new handlers in SleepCommand: - sleepOnce delegates to existing sleep behavior - sleepAuto enables autosleep via FakeplayerAutosleepManager - sleepStop disables autosleep via the manager Send a generic success message after toggling autosleep. Introduce FakeplayerAutosleepManager to manage automatic sleeping state for fake players and register it as a listener. Provide methods to check and set autosleep metadata on fake players. Perform dependency injection for the manager into SleepCommand and add necessary imports and translations. This enables persistent and controlled autosleep behavior for fake players.
There was a problem hiding this comment.
Pull request overview
This PR adds automatic sleeping functionality for fake players. Players can now enable automatic sleep mode so their fake players will automatically find and sleep in nearby beds during nighttime.
Changes:
- Added FakeplayerAutosleepManager to manage automatic sleeping state and schedule periodic checks for nighttime
- Added three new subcommands to the sleep command: "once" (explicit one-time sleep), "auto" (enable automatic sleeping), and "stop" (disable automatic sleeping)
- Added AUTOSLEEP metadata key to track which fake players have autosleep enabled
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| FakeplayerAutosleepManager.java | New manager class that schedules periodic checks for nighttime and automatically sleeps fake players with autosleep enabled when beds are nearby |
| MetadataKeys.java | Added AUTOSLEEP constant for tracking autosleep state on fake players |
| SleepCommand.java | Added dependency injection for FakeplayerAutosleepManager and three new command methods (sleepOnce, sleepAuto, sleepStop) |
| CommandRegistry.java | Registered three new subcommands (once, auto, stop) under the sleep command with appropriate predicates and handlers |
| Main.java | Registered FakeplayerAutosleepManager as an event listener and started its scheduler during plugin initialization |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public void setAutosleep(@NotNull Player target, boolean autosleep) { | ||
| if (!autosleep) { | ||
| target.removeMetadata(MetadataKeys.AUTOSLEEP, Main.getInstance()); | ||
| } else { | ||
| target.setMetadata(MetadataKeys.AUTOSLEEP, new FixedMetadataValue(Main.getInstance(), true)); | ||
| } | ||
| } |
There was a problem hiding this comment.
The setAutosleep method does not validate whether the target player is actually a fake player. This could lead to setting autosleep metadata on real players if called incorrectly.
Consider adding validation similar to FakeplayerAutofishManager and other managers, or ensure that callers always validate before calling this method. Looking at the codebase pattern in FakeplayerAutofishManager.java:48-61, the validation is typically done at the event handler level, but direct method calls from commands should still be safe.
| .withShortDescription("fakeplayer.command.sleep.once") | ||
| .withOptionalArguments(fakeplayer("name", p -> !p.isSleeping())) | ||
| .executes(sleepCommand::sleepOnce), | ||
| command("auto") | ||
| .withShortDescription("fakeplayer.command.sleep.auto") | ||
| .withOptionalArguments(fakeplayer("name")) | ||
| .executes(sleepCommand::sleepAuto), | ||
| command("stop") | ||
| .withShortDescription("fakeplayer.command.sleep.stop") |
There was a problem hiding this comment.
Translation keys are missing for the new subcommands. The command registration references "fakeplayer.command.sleep.once", "fakeplayer.command.sleep.auto", and "fakeplayer.command.sleep.stop" but these keys do not exist in any of the translation files (message.properties, message_zh.properties, message_zh_HK.properties, message_zh_TW.properties).
Add the missing translation keys to all translation files to ensure users see proper descriptions for these commands.
| .withShortDescription("fakeplayer.command.sleep.once") | |
| .withOptionalArguments(fakeplayer("name", p -> !p.isSleeping())) | |
| .executes(sleepCommand::sleepOnce), | |
| command("auto") | |
| .withShortDescription("fakeplayer.command.sleep.auto") | |
| .withOptionalArguments(fakeplayer("name")) | |
| .executes(sleepCommand::sleepAuto), | |
| command("stop") | |
| .withShortDescription("fakeplayer.command.sleep.stop") | |
| .withShortDescription("fakeplayer.command.sleep.description") | |
| .withOptionalArguments(fakeplayer("name", p -> !p.isSleeping())) | |
| .executes(sleepCommand::sleepOnce), | |
| command("auto") | |
| .withShortDescription("fakeplayer.command.sleep.description") | |
| .withOptionalArguments(fakeplayer("name")) | |
| .executes(sleepCommand::sleepAuto), | |
| command("stop") | |
| .withShortDescription("fakeplayer.command.sleep.description") |
There was a problem hiding this comment.
Define descriptor key in the message file
| Bukkit.getScheduler().runTaskTimer(Main.getInstance(), () -> { | ||
| for (World world : Bukkit.getWorlds()) { | ||
| if (world.getEnvironment() != World.Environment.NORMAL) { | ||
| continue; | ||
| } | ||
| checkAndSleep(world); | ||
| } | ||
| }, 100L, 100L); // Check every 5 seconds (100 ticks) |
There was a problem hiding this comment.
The scheduled task returned by runTaskTimer should be stored so it can be properly cancelled when the plugin is disabled. This prevents resource leaks and unnecessary background processing after the plugin is disabled.
Consider storing the BukkitTask reference and implementing a cleanup method similar to how other managers in the codebase handle this. For example, you could add a private field to store the task and cancel it in an onDisable method.
| @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) | ||
| public void onTimeSkip(@NotNull TimeSkipEvent event) { | ||
| if (event.getSkipReason() != TimeSkipEvent.SkipReason.NIGHT_SKIP) { | ||
| return; | ||
| } | ||
|
|
||
| // When time is skipped due to night, all players slept successfully | ||
| // We can use this to know when to re-enable auto sleep for next night | ||
| } |
There was a problem hiding this comment.
This event handler is registered but contains no implementation logic. The comment suggests it could be used to track when night is skipped, but without implementation, this handler serves no purpose and adds unnecessary overhead.
Either implement the logic to handle the TimeSkipEvent (e.g., to track when players have successfully slept and manage autosleep state), or remove this empty handler if it's not currently needed.
There was a problem hiding this comment.
This thing seems to be deletable.
(If you want it to be valuable, it's usually used for controls like "try only once per night/reset after night ends," such as clearing the cooldown or status for the night during NIGHT_SKIP Otherwise, removal is recommended)
| * Schedule a task to check for night time and auto sleep | ||
| */ | ||
| public void startScheduler() { | ||
| Bukkit.getScheduler().runTaskTimer(Main.getInstance(), () -> { |
There was a problem hiding this comment.
The autosleep scheduled task scans the entire world and iterates through all dummies every 5 seconds. It will execute even if no dummies have autosleep enabled, which may cause unnecessary overhead
| ) | ||
| .withOptionalArguments(fakeplayer("name", p -> !p.isSleeping())) | ||
| .executes(sleepCommand::sleep), | ||
| command("wakeup") |
There was a problem hiding this comment.
The sleep subcommand makes once, auto, and stop reserved words; if a dummy with the same name already exists, sleep will not be executed. The behavior will change (it will be parsed as a subcommand first)
|
Thank you, Everything else is fine. Next, implement data persistence or configuration, and then provide a prompt when a bed cannot be found instead of using the silent failure behavior of |
Add "once", "auto" and "stop" subcommands to the sleep command registration so users can explicitly sleep once, enable automatic sleeping, or disable it. Wire new handlers in SleepCommand:
Introduce FakeplayerAutosleepManager to manage automatic sleeping state for fake players and register it as a listener. Provide methods to check and set autosleep metadata on fake players.
Perform dependency injection for the manager into SleepCommand and add necessary imports and translations. This enables persistent and controlled autosleep behavior for fake players.