diff --git a/README.md b/README.md index bb9e180..7e69ed1 100644 --- a/README.md +++ b/README.md @@ -13,28 +13,28 @@ ## 命令 -+ /fp ? - 查看帮助手册 -+ /fp reload - 重载配置文件 -+ /fp create - 创建一个假人 -+ /fp kill - 移除假人 -+ /fp tp - 传送到假人身边 -+ /fp tps - 与假人交换位置 -+ /fp tphere - 将假人传送到自己身边 -+ /fp health - 查看假人的生命值 -+ /fp exp - 查看假人的经验值 -+ /fp expme - 转移假人的经验值 -+ /fp config - 玩家个性化配置 -+ /fp config set - 设置个性化配置 -+ /fp config get - 查看个性化配置 -+ /fp drop - 丢弃手上物品 -+ /fp dropinv - 丢弃背包物品 -+ /fp look - 让假人看向指定位置 -+ /fp move - 让假人移动 -+ /fp turn - 让假人转身 -+ /fp jump - 让假人跳跃 -+ /fp attack - 让假人点击鼠标左键 **(实验性)** -+ /fp use - 让假人点击鼠标右键 **(实验性)** -+ /fp cmd - 让假人执行他有权限执行的命令 ++ `/fp spawn [世界] [位置]` - 创建假人 ++ `/fp kill [假人]` - 移除假人 ++ `/fp list [页码] [数量]` - 查看所有假人 ++ `/fp distance` - 查看与假人的距离 ++ `/fp tp [假人]` - 传送到假人身边 ++ `/fp tphere [假人]` - 将假人传送到身边 ++ `/fp tps [假人]` - 与假人交换位置 ++ `/fp config get <配置项>` - 查看配置项 ++ `/fp config set <配置项> <配置值>` - 设置配置项 ++ `/fp health [假人]` - 查看生命值 ++ `/fp exp [假人]` - 查看经验值 ++ `/fp expme [假人]` - 转移经验值 ++ `/fp attack (once | continuous | interval | stop) [假人]` - 攻击/破坏 ++ `/fp use (once | continuous | interval | stop) [假人]` - 使用/交互/放置 ++ `/fp jump (once | continuous | interval | stop) [假人]` - 跳跃 ++ `/fp drop [假人] [-a|--all]` - 丢弃手上物品 ++ `/fp dropinv [假人]` - 丢弃背包物品 ++ `/fp look (north | south | east| west | up | down | at) [假人]` - 看向指定位置 ++ `/fp turn (left | right | back | to) [假人]` - 转身到指定位置 ++ `/fp move (forward | backward | left | right) [假人]` - 移动假人 ++ `/fp cmd <假人>` - 执行命令 ++ `/fp reload` - 重载配置文件 此外,假人是一个模拟玩家,因此可以被任何指令所识别比如 `kick`, `tp`, `ban` 等等 diff --git a/src/main/java/io/github/hello09x/fakeplayer/command/ActionCommand.java b/src/main/java/io/github/hello09x/fakeplayer/command/ActionCommand.java index 43e76eb..0d30779 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/command/ActionCommand.java +++ b/src/main/java/io/github/hello09x/fakeplayer/command/ActionCommand.java @@ -42,7 +42,12 @@ public class ActionCommand extends AbstractCommand { return (sender, args) -> action(sender, args, action, setting.clone()); } - public void action(@NotNull CommandSender sender, @NotNull CommandArguments args, @NotNull Action action, @NotNull ActionSetting setting) throws WrapperCommandSyntaxException { + public void action( + @NotNull CommandSender sender, + @NotNull CommandArguments args, + @NotNull Action action, + @NotNull ActionSetting setting + ) throws WrapperCommandSyntaxException { var target = getTarget(sender, args); actionManager.setAction(target, action, setting); diff --git a/src/main/java/io/github/hello09x/fakeplayer/command/Commands.java b/src/main/java/io/github/hello09x/fakeplayer/command/Commands.java index 115c8bc..3b0f386 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/command/Commands.java +++ b/src/main/java/io/github/hello09x/fakeplayer/command/Commands.java @@ -55,7 +55,7 @@ public class Commands { "§6/fp look (north|south|east|west|up|down|at) [假人] §7- §f看向指定位置", "§6/fp turn (left|right|back|to) [假人] §7- §f转身到指定位置", "§6/fp move (forward|backward|left|right) [假人] §7- §f移动假人", - "§6/fp cmd §7- §f执行命令", + "§6/fp cmd <假人> <命令> §7- §f执行命令", "§6/fp reload §7- §f重载配置文件" ) .withSubcommands( diff --git a/src/main/java/io/github/hello09x/fakeplayer/command/ConfigCommand.java b/src/main/java/io/github/hello09x/fakeplayer/command/ConfigCommand.java index e2575a6..b289bde 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/command/ConfigCommand.java +++ b/src/main/java/io/github/hello09x/fakeplayer/command/ConfigCommand.java @@ -24,7 +24,7 @@ public class ConfigCommand extends AbstractCommand { private final UserConfigRepository repository = UserConfigRepository.instance; - public static Argument> config(String nodeName) { + public static Argument> config(@NotNull String nodeName) { return new CustomArgument<>(new StringArgument(nodeName), info -> { var arg = info.currentInput(); try { @@ -35,7 +35,7 @@ public class ConfigCommand extends AbstractCommand { }).replaceSuggestions(ArgumentSuggestions.strings(Arrays.stream(Configs.values()).map(Config::name).toList())); } - public static Argument configValue(String configNodeName, String nodeName) { + public static Argument configValue(@NotNull String configNodeName, @NotNull String nodeName) { return new CustomArgument<>(new StringArgument(nodeName), info -> { @SuppressWarnings("unchecked") var config = Objects.requireNonNull((Config) info.previousArgs().get(configNodeName)); diff --git a/src/main/java/io/github/hello09x/fakeplayer/command/SpawnCommand.java b/src/main/java/io/github/hello09x/fakeplayer/command/SpawnCommand.java index 6316232..7562261 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/command/SpawnCommand.java +++ b/src/main/java/io/github/hello09x/fakeplayer/command/SpawnCommand.java @@ -34,7 +34,7 @@ public class SpawnCommand extends AbstractCommand { MathUtils.round(location.getZ(), 0.5)); } - public void spawn(@NotNull CommandSender sender, CommandArguments args) { + public void spawn(@NotNull CommandSender sender, @NotNull CommandArguments args) { var world = (World) args.get("world"); var location = (Location) args.get("location"); if (world == null || location == null) { @@ -48,7 +48,7 @@ public class SpawnCommand extends AbstractCommand { location.setWorld(world); } - var fakePlayer = fakeplayerManager.spawn(sender, location.clone()); + var fakePlayer = fakeplayerManager.spawn(sender, location); if (fakePlayer != null) { sender.sendMessage(textOfChildren( text("你创建了假人 ", GRAY), @@ -59,7 +59,7 @@ public class SpawnCommand extends AbstractCommand { } } - public void kill(@NotNull CommandSender sender, CommandArguments args) { + public void kill(@NotNull CommandSender sender, @NotNull CommandArguments args) { @SuppressWarnings("unchecked") var targets = (List) args.get("targets"); if (targets == null) { diff --git a/src/main/java/io/github/hello09x/fakeplayer/command/TpCommand.java b/src/main/java/io/github/hello09x/fakeplayer/command/TpCommand.java index 03250d2..9fb0d11 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/command/TpCommand.java +++ b/src/main/java/io/github/hello09x/fakeplayer/command/TpCommand.java @@ -2,14 +2,10 @@ package io.github.hello09x.fakeplayer.command; import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException; import dev.jorel.commandapi.executors.CommandArguments; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; +import io.github.hello09x.fakeplayer.util.Teleportor; import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerTeleportEvent; import org.jetbrains.annotations.NotNull; -import static org.bukkit.Sound.ENTITY_ENDERMAN_TELEPORT; - public class TpCommand extends AbstractCommand { public final static TpCommand instance = new TpCommand(); @@ -27,19 +23,15 @@ public class TpCommand extends AbstractCommand { public void tps(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException { var target = getTarget(sender, args); - var l1 = sender.getLocation(); - var l2 = target.getLocation(); + var l1 = sender.getLocation().clone(); + var l2 = target.getLocation().clone(); - target.teleport(l1, PlayerTeleportEvent.TeleportCause.PLUGIN); - l1.getWorld().playSound(l1, Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F); - - sender.teleport(l2, PlayerTeleportEvent.TeleportCause.PLUGIN); - l2.getWorld().playSound(l2, Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F); + Teleportor.teleportAndSound(target, l1); + Teleportor.teleportAndSound(sender, l2); } private void teleport(@NotNull Player from, @NotNull Player to) { - from.teleport(to.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); - to.getLocation().getWorld().playSound(to.getLocation(), ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F); + Teleportor.teleportAndSound(from, to.getLocation()); } diff --git a/src/main/java/io/github/hello09x/fakeplayer/manager/FakeplayerManager.java b/src/main/java/io/github/hello09x/fakeplayer/manager/FakeplayerManager.java index 4691424..febd58d 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/manager/FakeplayerManager.java +++ b/src/main/java/io/github/hello09x/fakeplayer/manager/FakeplayerManager.java @@ -9,11 +9,11 @@ import io.github.hello09x.fakeplayer.repository.UsedIdRepository; import io.github.hello09x.fakeplayer.repository.UserConfigRepository; import io.github.hello09x.fakeplayer.repository.model.Configs; import io.github.hello09x.fakeplayer.util.AddressUtils; +import io.github.hello09x.fakeplayer.util.Teleportor; import io.github.hello09x.fakeplayer.util.Unwrapper; import net.kyori.adventure.text.format.Style; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Sound; import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.entity.Player; @@ -94,7 +94,7 @@ public class FakeplayerManager { return null; } - var seqname = nameManager.take(creator); + var sn = nameManager.take(creator); boolean invulnerable = true, lookAtEntity = true, collidable = true, pickupItems = true; if (creator instanceof Player p) { var creatorId = p.getUniqueId(); @@ -107,16 +107,16 @@ public class FakeplayerManager { var player = new FakePlayer( creator.getName(), ((CraftServer) Bukkit.getServer()).getServer(), - generateId(seqname.name()), - seqname.name() + generateId(sn.name()), + sn.name() ); var bukkitPlayer = player.getBukkitPlayer(); Metadatas.CREATOR.set(bukkitPlayer, creator.getName()); Metadatas.CREATOR_IP.set(bukkitPlayer, AddressUtils.getAddress(creator)); - Metadatas.NAME_SOURCE.set(bukkitPlayer, seqname.source()); - Metadatas.NAME_SEQUENCE.set(bukkitPlayer, seqname.sequence()); - bukkitPlayer.playerListName(text(creator.getName() + "的假人").style(Style.style(GRAY, ITALIC))); + Metadatas.NAME_SOURCE.set(bukkitPlayer, sn.source()); + Metadatas.NAME_SEQUENCE.set(bukkitPlayer, sn.sequence()); + bukkitPlayer.playerListName(text(bukkitPlayer.getName() + " (假人)").style(Style.style(GRAY, ITALIC))); player.spawn(invulnerable, collidable, lookAtEntity, pickupItems); @@ -124,29 +124,30 @@ public class FakeplayerManager { dispatchCommands(bukkitPlayer, properties.getPreparingCommands()); performCommands(bukkitPlayer, properties.getSelfCommands()); - bukkitPlayer.teleport(spawnAt); // 当前 tick 必须传到出生点否则无法触发区块刷新 - spawnAt.getWorld().playSound(spawnAt, Sound.ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F); + spawnAt = spawnAt.clone(); + Teleportor.teleportAndSound(bukkitPlayer, spawnAt); // 当前 tick 必须传到出生点否则无法触发区块刷新 + ensureSpawnpoint(bukkitPlayer, spawnAt); // 防止别的插件比如 `multicore` 把他带离出生点 - // 可能被别的插件干预 - // 在下一 tick 里探测 + return bukkitPlayer; + } + + private void ensureSpawnpoint(@NotNull Player player, @NotNull Location spawnpoint) { new BukkitRunnable() { @Override public void run() { - if (spawnAt.distance(bukkitPlayer.getLocation()) < 16) { + if (spawnpoint.getWorld().equals(spawnpoint.getWorld()) && spawnpoint.distance(player.getLocation()) < 16) { return; } - bukkitPlayer.teleport(spawnAt.getWorld().getSpawnLocation()); + player.teleport(spawnpoint.getWorld().getSpawnLocation()); new BukkitRunnable() { @Override public void run() { - bukkitPlayer.teleport(spawnAt); + player.teleport(spawnpoint); } }.runTaskLater(Main.getInstance(), 1); } }.runTaskLater(Main.getInstance(), 1); - - return bukkitPlayer; } public @Nullable Player get(@NotNull CommandSender creator, @NotNull String name) { diff --git a/src/main/java/io/github/hello09x/fakeplayer/manager/NameManager.java b/src/main/java/io/github/hello09x/fakeplayer/manager/NameManager.java index 8dc365c..36bf6f1 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/manager/NameManager.java +++ b/src/main/java/io/github/hello09x/fakeplayer/manager/NameManager.java @@ -14,14 +14,12 @@ import java.util.logging.Logger; public class NameManager { public final static NameManager instance = new NameManager(); - + private final static Logger log = Main.getInstance().getLogger(); + private final static int MAX_LENGTH = 16; // mojang required private final FakeplayerProperties properties = FakeplayerProperties.instance; - private final ConcurrentHashMap nameSources = new ConcurrentHashMap<>(); - private final static Logger log = Main.getInstance().getLogger(); - - public SequenceName take(CommandSender creator) { + public @NotNull SequenceName take(CommandSender creator) { var source = properties.getNameTemplate(); if (source.isBlank()) { source = creator.getName(); @@ -33,8 +31,8 @@ public class NameManager { var suffix = "_" + (seq + 1); String name; - if (source.length() + suffix.length() > 16) { - name = source.substring(0, (16 - suffix.length())); + if (source.length() + suffix.length() > MAX_LENGTH) { + name = source.substring(0, (MAX_LENGTH - suffix.length())); } else { name = source; } @@ -52,7 +50,8 @@ public class NameManager { ); } - var name = "FAKE_" + RandomStringUtils.random(11, true, true); + var name = "_fp_"; + name = name + RandomStringUtils.random(MAX_LENGTH - name.length(), true, true); log.warning("Could not generate a name which is never used at this server after 10 tries, using random player name as fallback: " + name); return new SequenceName("random", 0, name); } diff --git a/src/main/java/io/github/hello09x/fakeplayer/manager/NameSource.java b/src/main/java/io/github/hello09x/fakeplayer/manager/NameSource.java index be0c8d0..7d0c84c 100644 --- a/src/main/java/io/github/hello09x/fakeplayer/manager/NameSource.java +++ b/src/main/java/io/github/hello09x/fakeplayer/manager/NameSource.java @@ -20,8 +20,7 @@ public class NameSource { this(0); } - public - synchronized int pop() { + public synchronized int pop() { if (names.isEmpty()) { var newCapacity = capacity * 2; for (int i = capacity; i < newCapacity; i++) { diff --git a/src/main/java/io/github/hello09x/fakeplayer/util/Teleportor.java b/src/main/java/io/github/hello09x/fakeplayer/util/Teleportor.java new file mode 100644 index 0000000..aeba3e4 --- /dev/null +++ b/src/main/java/io/github/hello09x/fakeplayer/util/Teleportor.java @@ -0,0 +1,16 @@ +package io.github.hello09x.fakeplayer.util; + +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; + +public class Teleportor { + + public static void teleportAndSound(@NotNull Entity entity, @NotNull Location location) { + entity.teleport(location); + location.getWorld().playSound(location, Sound.ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F);; + } + + +}