mirror of
https://github.com/tanyaofei/minecraft-fakeplayer.git
synced 2025-09-14 11:16:46 +08:00
Compare commits
7 Commits
0e3769712e
...
1285cd9e81
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1285cd9e81 | ||
![]() |
2e6a3a34d9 | ||
![]() |
07340d3218 | ||
![]() |
6a3d880f17 | ||
![]() |
f261e16a23 | ||
![]() |
d4bc411536 | ||
![]() |
94abb23655 |
@ -47,7 +47,7 @@ This approach can let you preview new content when you are upgrading it.
|
|||||||
| /fp invsee | Open an inventory of a fake player | fakeplayer.command.invsee | Right-clicking on fake players has the same effect |
|
| /fp invsee | Open an inventory of a fake player | fakeplayer.command.invsee | Right-clicking on fake players has the same effect |
|
||||||
| /fp sleep | Sleep | fakeplayer.command.sleep | |
|
| /fp sleep | Sleep | fakeplayer.command.sleep | |
|
||||||
| /fp wakeup | Wake up | fakeplayer.command.wakeup | |
|
| /fp wakeup | Wake up | fakeplayer.command.wakeup | |
|
||||||
| /fp status | Show player status | fakeplayer.command.status | |
|
| /fp status | Show status | fakeplayer.command.status | |
|
||||||
| /fp respawn | Respawn a dead fake player | fakeplayer.command.respawn | Available when server config does not kick on fake player death |
|
| /fp respawn | Respawn a dead fake player | fakeplayer.command.respawn | Available when server config does not kick on fake player death |
|
||||||
| /fp tp | Teleport to a fake player | fakeplayer.command.tp | |
|
| /fp tp | Teleport to a fake player | fakeplayer.command.tp | |
|
||||||
| /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | |
|
| /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | |
|
||||||
@ -167,13 +167,13 @@ If your server does not restrict various player commands, you can use this direc
|
|||||||
4. Edit your `config.yml`, set `i18n.locale` to the name suffix which you just created such as `en_us`
|
4. Edit your `config.yml`, set `i18n.locale` to the name suffix which you just created such as `en_us`
|
||||||
5. Type `/fp reload-translation` to reload translation file. If you change `i18n.local`, you should `/fp reload` first
|
5. Type `/fp reload-translation` to reload translation file. If you change `i18n.local`, you should `/fp reload` first
|
||||||
|
|
||||||
**Make sure the translation file is encoding with UTF-8**
|
**Make sure the translation file is encoded with UTF-8**
|
||||||
|
|
||||||
# FAQs
|
# FAQs
|
||||||
|
|
||||||
## xxx lost connection: PacketEvents 2.0 failed to inject
|
## xxx lost connection: PacketEvents 2.0 failed to inject
|
||||||
|
|
||||||
Some plugin change the `Connection` of the fake player, You can set `prevent-kicking` to `ALWAYS` to solve it.
|
Some plugin changes the `Connection` of the fake player, You can set `prevent-kicking` to `ALWAYS` to solve it.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# config.yml
|
# config.yml
|
||||||
|
@ -221,4 +221,32 @@ public interface NMSServerPlayer {
|
|||||||
*/
|
*/
|
||||||
void swapItemWithOffhand();
|
void swapItemWithOffhand();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加标签
|
||||||
|
* @param tag 标签
|
||||||
|
* @return 是否添加成功
|
||||||
|
*/
|
||||||
|
boolean addTag(@NotNull String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除标签
|
||||||
|
* @param tag 标签
|
||||||
|
* @return 是否移除成功
|
||||||
|
*/
|
||||||
|
boolean removeTag(@NotNull String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否拥有标签
|
||||||
|
* @param tag 标签
|
||||||
|
* @return 是否拥有
|
||||||
|
*/
|
||||||
|
boolean hasTag(@NotNull String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有标签
|
||||||
|
* @return 标签集合
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
java.util.Set<String> getTags();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,8 @@ public class CommandRegistry {
|
|||||||
private DebugCommand debugCommand;
|
private DebugCommand debugCommand;
|
||||||
@Inject
|
@Inject
|
||||||
private StopCommand stopCommand;
|
private StopCommand stopCommand;
|
||||||
|
@Inject
|
||||||
|
private TagCommand tagCommand;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private FakeplayerConfig config;
|
private FakeplayerConfig config;
|
||||||
@ -457,8 +459,11 @@ public class CommandRegistry {
|
|||||||
text("message")
|
text("message")
|
||||||
)
|
)
|
||||||
.executes(debugCommand::sendPluginMessage)
|
.executes(debugCommand::sendPluginMessage)
|
||||||
)
|
),
|
||||||
|
command("tag")
|
||||||
|
.withPermission(Permission.basic)
|
||||||
|
.withShortDescription("管理假人标签")
|
||||||
|
.executesPlayer((player, args) -> tagCommand.getCommand().execute(player, args))
|
||||||
);
|
);
|
||||||
HelpCommand.generateHelpCommand(root, true);
|
HelpCommand.generateHelpCommand(root, true);
|
||||||
root.register();
|
root.register();
|
||||||
|
@ -66,9 +66,20 @@ public abstract class CommandSupports {
|
|||||||
public static @NotNull Argument<Player> fakeplayer(@NotNull String nodeName, @Nullable Predicate<Player> predicate) {
|
public static @NotNull Argument<Player> fakeplayer(@NotNull String nodeName, @Nullable Predicate<Player> predicate) {
|
||||||
return new CustomArgument<>(new StringArgument(nodeName), info -> {
|
return new CustomArgument<>(new StringArgument(nodeName), info -> {
|
||||||
var sender = info.sender();
|
var sender = info.sender();
|
||||||
var target = sender.isOp()
|
var input = info.currentInput();
|
||||||
? manager.get(info.currentInput())
|
Player target = null;
|
||||||
: manager.get(sender, info.currentInput());
|
if (input.startsWith("*")) {
|
||||||
|
// 标签选择,返回第一个匹配的假人
|
||||||
|
var tag = input.substring(1);
|
||||||
|
var list = manager.getByTag(sender, tag);
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
target = list.get(0).getPlayer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = sender.isOp()
|
||||||
|
? manager.get(input)
|
||||||
|
: manager.get(sender, input);
|
||||||
|
}
|
||||||
if (predicate != null && target != null && !predicate.test(target)) {
|
if (predicate != null && target != null && !predicate.test(target)) {
|
||||||
target = null;
|
target = null;
|
||||||
}
|
}
|
||||||
@ -76,16 +87,17 @@ public abstract class CommandSupports {
|
|||||||
}).replaceSuggestions(ArgumentSuggestions.strings(info -> {
|
}).replaceSuggestions(ArgumentSuggestions.strings(info -> {
|
||||||
var sender = info.sender();
|
var sender = info.sender();
|
||||||
var arg = info.currentArg();
|
var arg = info.currentArg();
|
||||||
|
|
||||||
var targets = sender.isOp()
|
var targets = sender.isOp()
|
||||||
? manager.getAll(predicate)
|
? manager.getAll(predicate)
|
||||||
: manager.getAll(sender, predicate);
|
: manager.getAll(sender, predicate);
|
||||||
|
|
||||||
var names = targets.stream().map(Player::getName);
|
var names = targets.stream().map(Player::getName);
|
||||||
|
// 标签补全
|
||||||
|
var tagNames = manager.getByTag(sender, arg.startsWith("*") ? arg.substring(1) : arg)
|
||||||
|
.stream().map(fp -> "*" + arg).distinct();
|
||||||
|
names = Stream.concat(names, tagNames);
|
||||||
if (!arg.isEmpty()) {
|
if (!arg.isEmpty()) {
|
||||||
names = names.filter(n -> n.toLowerCase().contains(arg));
|
names = names.filter(n -> n.toLowerCase().contains(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
return names.toArray(String[]::new);
|
return names.toArray(String[]::new);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -99,29 +111,31 @@ public abstract class CommandSupports {
|
|||||||
return new CustomArgument<List<Player>, String>(new StringArgument(nodeName), info -> {
|
return new CustomArgument<List<Player>, String>(new StringArgument(nodeName), info -> {
|
||||||
var sender = info.sender();
|
var sender = info.sender();
|
||||||
var arg = info.currentInput();
|
var arg = info.currentInput();
|
||||||
|
|
||||||
if (arg.equals("-a")) {
|
if (arg.equals("-a")) {
|
||||||
return manager.getAll(sender);
|
return manager.getAll(sender);
|
||||||
}
|
}
|
||||||
|
if (arg.startsWith("*")) {
|
||||||
|
var tag = arg.substring(1);
|
||||||
|
return manager.getByTag(sender, tag).stream().map(Fakeplayer::getPlayer).toList();
|
||||||
|
}
|
||||||
var target = sender.isOp()
|
var target = sender.isOp()
|
||||||
? manager.get(arg)
|
? manager.get(arg)
|
||||||
: manager.get(sender, arg);
|
: manager.get(sender, arg);
|
||||||
|
|
||||||
return target == null ? Collections.emptyList() : Collections.singletonList(target);
|
return target == null ? Collections.emptyList() : Collections.singletonList(target);
|
||||||
}).replaceSuggestions(ArgumentSuggestions.strings(info -> {
|
}).replaceSuggestions(ArgumentSuggestions.strings(info -> {
|
||||||
var sender = info.sender();
|
var sender = info.sender();
|
||||||
var arg = info.currentArg().toLowerCase();
|
var arg = info.currentArg().toLowerCase();
|
||||||
|
|
||||||
var fakes = sender.isOp()
|
var fakes = sender.isOp()
|
||||||
? manager.getAll()
|
? manager.getAll()
|
||||||
: manager.getAll(sender);
|
: manager.getAll(sender);
|
||||||
|
|
||||||
var names = Stream.concat(fakes.stream().map(Player::getName), Stream.of("-a"));
|
var names = Stream.concat(fakes.stream().map(Player::getName), Stream.of("-a"));
|
||||||
|
// 标签补全
|
||||||
|
var tagNames = manager.getByTag(sender, arg.startsWith("*") ? arg.substring(1) : arg)
|
||||||
|
.stream().map(fp -> "*" + arg).distinct();
|
||||||
|
names = Stream.concat(names, tagNames);
|
||||||
if (!arg.isEmpty()) {
|
if (!arg.isEmpty()) {
|
||||||
names = names.filter(n -> n.toLowerCase().contains(arg));
|
names = names.filter(n -> n.toLowerCase().contains(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
return names.toArray(String[]::new);
|
return names.toArray(String[]::new);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package io.github.hello09x.fakeplayer.core.command.impl;
|
||||||
|
|
||||||
|
import dev.jorel.commandapi.CommandAPICommand;
|
||||||
|
import dev.jorel.commandapi.CommandPermission;
|
||||||
|
import dev.jorel.commandapi.arguments.StringArgument;
|
||||||
|
import io.github.hello09x.fakeplayer.core.entity.Fakeplayer;
|
||||||
|
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static net.kyori.adventure.text.Component.text;
|
||||||
|
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
||||||
|
|
||||||
|
public class TagCommand {
|
||||||
|
private final FakeplayerManager manager;
|
||||||
|
|
||||||
|
public TagCommand(FakeplayerManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandAPICommand getCommand() {
|
||||||
|
return new CommandAPICommand("tag")
|
||||||
|
.withPermission(CommandPermission.fromString("fakeplayer.command.tag"))
|
||||||
|
.withSubcommand(new CommandAPICommand("add")
|
||||||
|
.withArguments(new StringArgument("tag"))
|
||||||
|
.withOptionalArguments(new StringArgument("--all").setOptional(true))
|
||||||
|
.executesPlayer((player, args) -> {
|
||||||
|
String tag = (String) args.get(0);
|
||||||
|
boolean all = args.size() > 1 && "--all".equals(args.get(1));
|
||||||
|
if (all) {
|
||||||
|
var list = manager.getAll(player);
|
||||||
|
int success = 0, exist = 0;
|
||||||
|
for (var p : list) {
|
||||||
|
var fake = manager.getByOwner(p);
|
||||||
|
if (fake != null) {
|
||||||
|
if (fake.getHandle().addTag(tag)) success++;
|
||||||
|
else exist++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.sendMessage(text("批量添加标签: " + tag + ",成功 " + success + " 个,已存在 " + exist + " 个", GREEN));
|
||||||
|
} else {
|
||||||
|
Fakeplayer fake = getSelfFakeplayer(player);
|
||||||
|
if (fake == null) return;
|
||||||
|
if (fake.getHandle().addTag(tag)) {
|
||||||
|
player.sendMessage(text("已添加标签: " + tag, GREEN));
|
||||||
|
} else {
|
||||||
|
player.sendMessage(text("标签已存在: " + tag, YELLOW));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.withSubcommand(new CommandAPICommand("remove")
|
||||||
|
.withArguments(new StringArgument("tag"))
|
||||||
|
.executesPlayer((player, args) -> {
|
||||||
|
Fakeplayer fake = getSelfFakeplayer(player);
|
||||||
|
if (fake == null) return;
|
||||||
|
String tag = (String) args.get(0);
|
||||||
|
if (fake.getHandle().removeTag(tag)) {
|
||||||
|
player.sendMessage(text("已移除标签: " + tag, GREEN));
|
||||||
|
} else {
|
||||||
|
player.sendMessage(text("标签不存在: " + tag, YELLOW));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.withSubcommand(new CommandAPICommand("list")
|
||||||
|
.executesPlayer((player, args) -> {
|
||||||
|
Fakeplayer fake = getSelfFakeplayer(player);
|
||||||
|
if (fake == null) return;
|
||||||
|
Set<String> tags = fake.getHandle().getTags();
|
||||||
|
if (tags.isEmpty()) {
|
||||||
|
player.sendMessage(text("当前没有标签", GRAY));
|
||||||
|
} else {
|
||||||
|
player.sendMessage(text("标签: " + String.join(", ", tags), AQUA));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Fakeplayer getSelfFakeplayer(@NotNull Player player) {
|
||||||
|
Fakeplayer fake = manager.getByOwner(player);
|
||||||
|
if (fake == null) {
|
||||||
|
player.sendMessage(text("你没有可操作的假人", RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return fake;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.hello09x.fakeplayer.core.command.impl;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||||
|
|
||||||
|
public class TagCommandProvider implements Provider<TagCommand> {
|
||||||
|
@Inject
|
||||||
|
private FakeplayerManager manager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagCommand get() {
|
||||||
|
return new TagCommand(manager);
|
||||||
|
}
|
||||||
|
}
|
@ -10,16 +10,15 @@ import io.github.hello09x.fakeplayer.core.Main;
|
|||||||
import io.github.hello09x.fakeplayer.core.repository.model.Feature;
|
import io.github.hello09x.fakeplayer.core.repository.model.Feature;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -52,6 +51,21 @@ public class FakeplayerConfig extends PluginConfig {
|
|||||||
*/
|
*/
|
||||||
private String nameTemplate;
|
private String nameTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 假人名称前缀
|
||||||
|
*/
|
||||||
|
private String namePrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称样式, 颜色
|
||||||
|
*/
|
||||||
|
private NamedTextColor nameStyleColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称样式, 格式
|
||||||
|
*/
|
||||||
|
private List<TextDecoration> nameStyleDecorations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建者玩家下线时是否跟随下线
|
* 创建者玩家下线时是否跟随下线
|
||||||
*/
|
*/
|
||||||
@ -187,6 +201,7 @@ public class FakeplayerConfig extends PluginConfig {
|
|||||||
this.namePattern = getNamePattern(file);
|
this.namePattern = getNamePattern(file);
|
||||||
this.preventKicking = this.getPreventKicking(file);
|
this.preventKicking = this.getPreventKicking(file);
|
||||||
this.nameTemplate = getNameTemplate(file);
|
this.nameTemplate = getNameTemplate(file);
|
||||||
|
this.namePrefix = file.getString("name-prefix", "");
|
||||||
this.lifespan = getLifespan(file);
|
this.lifespan = getLifespan(file);
|
||||||
this.allowCommands = file.getStringList("allow-commands")
|
this.allowCommands = file.getStringList("allow-commands")
|
||||||
.stream()
|
.stream()
|
||||||
@ -199,6 +214,8 @@ public class FakeplayerConfig extends PluginConfig {
|
|||||||
.collect(Collectors.toMap(Function.identity(), key -> file.getString("default-features." + key.name(), key.getDefaultOption())));
|
.collect(Collectors.toMap(Function.identity(), key -> file.getString("default-features." + key.name(), key.getDefaultOption())));
|
||||||
this.invseeImplement = ConfigUtils.getEnum(file, "invsee-implement", InvseeImplement.class, InvseeImplement.AUTO);
|
this.invseeImplement = ConfigUtils.getEnum(file, "invsee-implement", InvseeImplement.class, InvseeImplement.AUTO);
|
||||||
this.debug = file.getBoolean("debug", false);
|
this.debug = file.getBoolean("debug", false);
|
||||||
|
this.nameStyleColor = this.getNameStyleColor(file);
|
||||||
|
this.nameStyleDecorations = this.getNameStyleDecorations(file);
|
||||||
|
|
||||||
if (this.isConfigFileOutOfDate()) {
|
if (this.isConfigFileOutOfDate()) {
|
||||||
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
|
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
|
||||||
@ -262,4 +279,28 @@ public class FakeplayerConfig extends PluginConfig {
|
|||||||
return ConfigUtils.getEnum(file, "prevent-kicking", PreventKicking.class, PreventKicking.ON_SPAWNING);
|
return ConfigUtils.getEnum(file, "prevent-kicking", PreventKicking.class, PreventKicking.ON_SPAWNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NotNull NamedTextColor getNameStyleColor(@NotNull FileConfiguration file) {
|
||||||
|
var styles = Objects.requireNonNullElse(file.getString("name-style"), "").split(",\\s*");
|
||||||
|
var color = NamedTextColor.WHITE;
|
||||||
|
for (var style : styles) {
|
||||||
|
var c = NamedTextColor.NAMES.value(style);
|
||||||
|
if (c != null) {
|
||||||
|
color = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull List<TextDecoration> getNameStyleDecorations(@NotNull FileConfiguration file) {
|
||||||
|
var styles = Objects.requireNonNullElse(file.getString("name-style"), "").split(",\\s*");
|
||||||
|
var decorations = new ArrayList<TextDecoration>();
|
||||||
|
for (var style : styles) {
|
||||||
|
var decoration = TextDecoration.NAMES.value(style);
|
||||||
|
if (decoration != null) {
|
||||||
|
decorations.add(decoration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decorations;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName;
|
|||||||
import io.github.hello09x.fakeplayer.core.util.Attributes;
|
import io.github.hello09x.fakeplayer.core.util.Attributes;
|
||||||
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
|
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.attribute.AttributeInstance;
|
import org.bukkit.attribute.AttributeInstance;
|
||||||
@ -36,7 +37,6 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import static net.kyori.adventure.text.Component.text;
|
import static net.kyori.adventure.text.Component.text;
|
||||||
import static net.kyori.adventure.text.Component.translatable;
|
import static net.kyori.adventure.text.Component.translatable;
|
||||||
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
||||||
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
|
|
||||||
|
|
||||||
public class Fakeplayer {
|
public class Fakeplayer {
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ public class Fakeplayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupName() {
|
private void setupName() {
|
||||||
var displayName = text(player.getName(), GRAY, ITALIC);
|
var displayName = text(player.getName(), config.getNameStyleColor(), config.getNameStyleDecorations().toArray(new TextDecoration[0]));
|
||||||
player.playerListName(displayName);
|
player.playerListName(displayName);
|
||||||
player.displayName(displayName);
|
player.displayName(displayName);
|
||||||
player.customName(displayName);
|
player.customName(displayName);
|
||||||
|
@ -490,4 +490,26 @@ public class FakeplayerManager {
|
|||||||
Exceptions.suppress(Main.getInstance(), this.lagMonitor::shutdownNow);
|
Exceptions.suppress(Main.getInstance(), this.lagMonitor::shutdownNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家当前选中的假人(如果没有则取第一个自己创建的假人)
|
||||||
|
*/
|
||||||
|
public Fakeplayer getByOwner(Player player) {
|
||||||
|
Player selected = getSelection(player);
|
||||||
|
if (selected != null) {
|
||||||
|
return this.playerList.getByUUID(selected.getUniqueId());
|
||||||
|
}
|
||||||
|
// 没有选中则取第一个自己创建的
|
||||||
|
List<Fakeplayer> list = this.playerList.getByCreator(player.getName());
|
||||||
|
return list.isEmpty() ? null : list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家自己创建且带指定标签的所有假人
|
||||||
|
*/
|
||||||
|
public List<Fakeplayer> getByTag(@NotNull CommandSender owner, @NotNull String tag) {
|
||||||
|
return this.playerList.getByCreator(owner.getName())
|
||||||
|
.stream()
|
||||||
|
.filter(fp -> fp.getHandle().hasTag(tag))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import io.github.hello09x.fakeplayer.core.repository.FakeplayerProfileRepository
|
|||||||
import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
|
import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -118,6 +119,9 @@ public class NameManager {
|
|||||||
* @return 序列名
|
* @return 序列名
|
||||||
*/
|
*/
|
||||||
public @NotNull SequenceName getSpecifiedName(@NotNull String name) {
|
public @NotNull SequenceName getSpecifiedName(@NotNull String name) {
|
||||||
|
if (StringUtils.isNotBlank(config.getNamePrefix())) {
|
||||||
|
name = config.getNamePrefix().trim() + name;
|
||||||
|
}
|
||||||
if (name.startsWith("-")) {
|
if (name.startsWith("-")) {
|
||||||
throw new IllegalCustomNameException(translatable(
|
throw new IllegalCustomNameException(translatable(
|
||||||
"fakeplayer.spawn.error.name.start-with-illegal-character",
|
"fakeplayer.spawn.error.name.start-with-illegal-character",
|
||||||
@ -184,6 +188,9 @@ public class NameManager {
|
|||||||
source = creator.getName();
|
source = creator.getName();
|
||||||
}
|
}
|
||||||
source = source.replace("%c", creator.getName());
|
source = source.replace("%c", creator.getName());
|
||||||
|
if (StringUtils.isNotBlank(config.getNamePrefix())) {
|
||||||
|
source = config.getNamePrefix().trim() + source;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
var seq = nameSources.computeIfAbsent(source, ignored -> new NameSource(config.getPlayerLimit())).pop();
|
var seq = nameSources.computeIfAbsent(source, ignored -> new NameSource(config.getPlayerLimit())).pop();
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
# =========================================================================================================
|
# =========================================================================================================
|
||||||
# 【Important】【非常重要】
|
# 【Important】
|
||||||
# DO NOT EDIT config.temp.yml, copy it and rename to config.yml as your configuration file
|
# DO NOT EDIT config.tmpl.yml !!!
|
||||||
# 不要直接修改 config.tmpl.yml 文件,将这份文件复制并重命名为 config.yml 来作为配置文件
|
# Copy and rename this file to config.yml as your configuration file.
|
||||||
# 【Important】【非常重要】
|
# Any changes to this file will be reverted.
|
||||||
|
# -------------------------------------------------------------------------------------------------------
|
||||||
|
# 【非常重要】
|
||||||
|
# 不要修改 config.tmpl.yml !!!
|
||||||
|
# 复制此文件并重命名为 config.yml 来作为你的配置文件
|
||||||
|
# 此文件的任何修改将会在服务器启动时被还原
|
||||||
# =========================================================================================================
|
# =========================================================================================================
|
||||||
version: 18
|
|
||||||
|
# DO NOT EDIT
|
||||||
|
# 不要修改
|
||||||
|
version: 19
|
||||||
|
|
||||||
# 多国语言配置
|
# 多国语言配置
|
||||||
# 可选项: en, zh, zh_cn, zh_tw, zh_hk
|
# 可选项: en, zh, zh_cn, zh_tw, zh_hk
|
||||||
@ -60,10 +68,15 @@ lifespan: 0
|
|||||||
# Tips:
|
# Tips:
|
||||||
# 1. If this value contains characters other than alphabetic, numbers, and underscores, many vanilla commands will not be usable on them.
|
# 1. If this value contains characters other than alphabetic, numbers, and underscores, many vanilla commands will not be usable on them.
|
||||||
# 2. Characters longer than 16 characters will be truncated
|
# 2. Characters longer than 16 characters will be truncated
|
||||||
# 3. Can not start with '-'
|
# 3. Cannot start with '-'
|
||||||
# 4. It's not recommended, players may give them RES privileges, or put some items into their inventory.
|
# 4. It's not recommended, players may give them RES privileges, or put some items into their inventory.
|
||||||
name-template: ''
|
name-template: ''
|
||||||
|
|
||||||
|
# 假人名称前缀
|
||||||
|
# 自动生成的名称或者玩家创建时指定名称都将会追加前缀
|
||||||
|
# Name prefix.
|
||||||
|
# Prefix will be added to the generated name or specified name
|
||||||
|
name-prefix: ''
|
||||||
|
|
||||||
# 假人自定义名称允许的字符
|
# 假人自定义名称允许的字符
|
||||||
# 格式: 正则表达式
|
# 格式: 正则表达式
|
||||||
@ -74,6 +87,14 @@ name-template: ''
|
|||||||
# 3. 如果你改了正则表达式, 请确保它以 `^` 开头并且以 `$` 结尾
|
# 3. 如果你改了正则表达式, 请确保它以 `^` 开头并且以 `$` 结尾
|
||||||
name-pattern: '^[a-zA-Z0-9_]+$'
|
name-pattern: '^[a-zA-Z0-9_]+$'
|
||||||
|
|
||||||
|
# 假人在 Tab 上的名称样式
|
||||||
|
# Name style on Tab listing
|
||||||
|
# Options:
|
||||||
|
# black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray,
|
||||||
|
# dark_gray, blue, green, aqua, red, light_purple, yellow, white
|
||||||
|
# obfuscated, bold, strikethrough, underlined, italic
|
||||||
|
name-style: gray, italic
|
||||||
|
|
||||||
|
|
||||||
# 防止假人被其他插件踢掉, 这个选项用来兼容一些插件因为某些问题而踢掉假人
|
# 防止假人被其他插件踢掉, 这个选项用来兼容一些插件因为某些问题而踢掉假人
|
||||||
# 可选项:
|
# 可选项:
|
||||||
|
@ -251,4 +251,24 @@ public class NMSServerPlayerImpl implements NMSServerPlayer {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addTag(@NotNull String tag) {
|
||||||
|
return handle.addTag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeTag(@NotNull String tag) {
|
||||||
|
return handle.removeTag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTag(@NotNull String tag) {
|
||||||
|
return handle.getTags().contains(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull java.util.Set<String> getTags() {
|
||||||
|
return handle.getTags();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,11 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
|
|||||||
private boolean addChannel(@NotNull Player player, @NotNull String channel) {
|
private boolean addChannel(@NotNull Player player, @NotNull String channel) {
|
||||||
try {
|
try {
|
||||||
var method = player.getClass().getMethod("addChannel", String.class);
|
var method = player.getClass().getMethod("addChannel", String.class);
|
||||||
return (boolean) method.invoke(player, channel);
|
var ret = method.invoke(player, channel);
|
||||||
|
if (ret instanceof Boolean success) {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user