Skip to content

Commit 69dab4f

Browse files
authored
fix(command): /fp say 假人名字补全 + 修复 say stop 崩溃 (#66)
#64 /fp say once|continuous|interval 的 name 参数没有补全: - 新增 CommandSupports#fakeplayerName(String): 底层保持 StringArgument (保留 「单参数回退成消息」, /fp say once 1234 行为不变), 但挂上假人名字的 ArgumentSuggestions - 补全语义与 fakeplayer(...) 一致: op / 控制台列出全部, 普通玩家只列自己的 #65 /fp say stop 完全不可用: - say stop 的 name 来自假人参数 (Player), SayCommand 无条件强转 String -> ClassCastException - 不传名字时又被「消息不能为空」拦下 -> 该子命令两种写法都走不到停止逻辑 - 现按类型取名字, 且 stop 不再要求消息 均为本地实机验证 (Purpur 26.3 + CommandAPI 12.1.0)。 Co-authored-by: EndlessPixel <EndlessPixel@users.noreply.github.com>
1 parent e838300 commit 69dab4f

4 files changed

Lines changed: 46 additions & 12 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Changelog for FakePlayer CE. Version `fp.buildN` matches the git tag / GitHub Re
2020
- Fixed `follow-quiting-force` removing a creator's fake players even when the creator reconnected within the configured delay (quick reconnect, or coming back from another server) — the fake players are now kept when the creator is online again.
2121
- `/fp config` (including `config list` and `config set`) now works from the console — previously it failed with CommandAPI's internal error `This command has no implementations for terminalconsolecommandsender`, because the command was player-only. On the console `list` shows the global defaults, and operators / the console can target a specific player with the new optional `[player]` argument (e.g. `/fp config set autofish true <player>`), while regular players can still only configure themselves. `/fp config help` was added as well — it previously printed nothing at all.
2222
- Fixed `/fp say` (and `say continuous` / `say interval`) rejecting the name of a fake player owned by someone else with "you haven't spawned a fake player yet" — from the console this made it impossible to address any fake player by name at all. An explicitly given name is now resolved with the same rules as every other command: operators and the console may target any fake player, regular players only their own.
23+
- `/fp say once` / `continuous` / `interval` now TAB-complete fake player names — the name argument previously offered no suggestions at all (unlike every other command), because it is parsed as a plain string so that a single argument still falls back to being the message.
24+
- Fixed `/fp say stop` being entirely unusable: with a fake-player name it threw a `ClassCastException` ("An unexpected error occurred"), and without one it complained about an empty message instead of stopping. `stop` no longer requires a message; the single-argument message fallback of the other subcommands is unchanged.
2325

2426
### 中文
2527

@@ -31,6 +33,8 @@ Changelog for FakePlayer CE. Version `fp.buildN` matches the git tag / GitHub Re
3133
- 修复 `follow-quiting-force` 在创建者于延迟内重新上线(快速重连,或从其他服务器返回)时仍会清理其假人的问题;创建者已回到服务器时现在会保留假人。
3234
- `/fp config`(含 `config list` 与 `config set`)现在可以在控制台使用——此前会报 CommandAPI 的内部错误 `This command has no implementations for terminalconsolecommandsender`,因为该命令只注册了玩家执行器。控制台下 `list` 展示全局默认值;op / 控制台可用新增的可选 `[player]` 参数指定目标玩家(如 `/fp config set autofish true <玩家>`),普通玩家仍然只能配置自己。同时补上了 `/fp config help`——此前执行它完全没有输出。
3335
- 修复 `/fp say`(及 `say continuous` / `say interval`)指定「他人的假人名称」时报「你还没召唤假人呢」的问题——在控制台里这会导致完全无法按名字指定假人。显式给出的名字现按与其他命令一致的规则解析:op 与控制台可指定任意假人,普通玩家只能指定自己召唤的假人。
36+
- `/fp say once` / `continuous` / `interval` 的假人名字现在支持 TAB 补全——此前该参数没有任何补全(与其他命令不同),因为为了保留「单参数即消息」的回退,它按字符串解析。
37+
- 修复 `/fp say stop` 完全不可用的问题:带假人名字时会抛 `ClassCastException`(提示发生意外错误),不带名字时则报「消息不能为空」而不执行停止。`stop` 不再要求消息;其余子命令的「单参数回退成消息」行为不变。
3438

3539
## fp.build12 - 2026-09-26
3640

‎fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/CommandRegistry.java‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import dev.jorel.commandapi.CommandPermission;
66
import dev.jorel.commandapi.arguments.EntitySelectorArgument;
77
import dev.jorel.commandapi.arguments.GreedyStringArgument;
8-
import dev.jorel.commandapi.arguments.StringArgument;
98
import io.github.hello09x.devtools.command.HelpCommand;
109
import io.github.hello09x.devtools.core.translation.TranslatorUtils;
1110
import io.github.hello09x.fakeplayer.api.spi.ActionSetting;
@@ -504,20 +503,20 @@ public void register() {
504503
command("once")
505504
.withShortDescription("fakeplayer.command.say.once")
506505
.withOptionalArguments(
507-
new StringArgument("name"),
506+
fakeplayerName("name"),
508507
new GreedyStringArgument("message"))
509508
.executes(sayCommand.say(ActionSetting.once())),
510509
command("continuous")
511510
.withShortDescription("fakeplayer.command.say.continuous")
512511
.withOptionalArguments(
513-
new StringArgument("name"),
512+
fakeplayerName("name"),
514513
new GreedyStringArgument("message"))
515514
.executes(sayCommand.say(ActionSetting.continuous())),
516515
command("interval")
517516
.withShortDescription("fakeplayer.command.say.interval")
518517
.withOptionalArguments(
519518
int32("ticks", 1),
520-
new StringArgument("name"),
519+
fakeplayerName("name"),
521520
new GreedyStringArgument("message"))
522521
.executes((sender, args) -> {
523522
int interval = (int) args.getOptional("ticks").orElse(1);

‎fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/CommandSupports.java‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,28 @@ public abstract class CommandSupports {
101101
return fakeplayer(nodeName, null);
102102
}
103103

104+
/**
105+
* 与 {@link #fakeplayer(String)} 相同的假人名字补全, 但参数底层类型保持字符串。
106+
* <p>供 `/fp say once|continuous|interval` 使用: 这些子命令的「单参数回退成消息」
107+
* (如 {@code /fp say once 1234}) 依赖 name 是字符串, 不能换成 {@link CustomArgument}
108+
* 那种假人参数 (否则 1234 会因为解析不出假人而直接报错), 所以这里只额外挂上 TAB 补全。</p>
109+
*/
110+
public static @NotNull Argument<String> fakeplayerName(@NotNull String nodeName) {
111+
return new StringArgument(nodeName).replaceSuggestions(ArgumentSuggestions.strings(info -> {
112+
var sender = info.sender();
113+
var arg = info.currentArg().toLowerCase(Locale.ROOT);
114+
115+
var targets = sender.isOp()
116+
? manager().getAll()
117+
: manager().getAll(sender);
118+
119+
return targets.stream()
120+
.map(Player::getName)
121+
.filter(name -> name.toLowerCase(Locale.ROOT).contains(arg))
122+
.toArray(String[]::new);
123+
}));
124+
}
125+
104126
public static @NotNull Argument<List<Player>> fakeplayers(@NotNull String nodeName) {
105127
return new CustomArgument<List<Player>, String>(new StringArgument(nodeName), info -> {
106128
var sender = info.sender();

‎fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/impl/SayCommand.java‎

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.github.hello09x.fakeplayer.api.spi.ActionType;
88
import io.github.hello09x.fakeplayer.core.manager.action.ActionManager;
99
import org.bukkit.command.ConsoleCommandSender;
10+
import org.bukkit.entity.Player;
1011
import org.jetbrains.annotations.NotNull;
1112

1213
import static net.kyori.adventure.text.Component.translatable;
@@ -29,17 +30,25 @@ public SayCommand(ActionManager actionManager) {
2930
*/
3031
public @NotNull CommandExecutor say(@NotNull ActionSetting setting) {
3132
return (sender, args) -> {
32-
var name = (String) args.get("name");
33+
// name 在 once / continuous / interval 里是字符串 (为了兼容单参数回退成消息),
34+
// 在 stop 里则来自假人参数 (Player), 所以这里不能直接强转 String
35+
var rawName = args.get("name");
36+
var name = rawName instanceof Player player ? player.getName() : (String) rawName;
3337
var message = (String) args.get("message");
34-
// 单参数回退: fp say once 1234 -> 把 1234 当作消息, 作用于默认假人
35-
if (message == null || message.isBlank()) {
36-
if (name != null && !name.isBlank()) {
37-
message = name;
38-
} else {
39-
sender.sendMessage(translatable("fakeplayer.command.say.error.empty"));
40-
return;
38+
39+
// stop 只是停止动作, 不需要消息
40+
if (!setting.equals(ActionSetting.stop())) {
41+
// 单参数回退: fp say once 1234 -> 把 1234 当作消息, 作用于默认假人
42+
if (message == null || message.isBlank()) {
43+
if (name != null && !name.isBlank()) {
44+
message = name;
45+
} else {
46+
sender.sendMessage(translatable("fakeplayer.command.say.error.empty"));
47+
return;
48+
}
4149
}
4250
}
51+
4352
var fake = super.getFakeplayer(sender, args);
4453
var copy = setting.clone();
4554
copy.message = message;

0 commit comments

Comments
 (0)