mirror of
https://github.com/tanyaofei/minecraft-fakeplayer.git
synced 2025-09-14 11:16:46 +08:00
添加 look, jump, move 命令
This commit is contained in:
parent
703b13515a
commit
f9e19ae7f7
@ -28,6 +28,9 @@
|
||||
+ /fp config get - 查看个性化配置
|
||||
+ /fp drop - 丢弃手上物品
|
||||
+ /fp dropinv - 丢弃背包物品
|
||||
+ /fp look - 让假人看向指定位置
|
||||
+ /fp move - 让家人移动
|
||||
+ /fp jump - 让假人跳跃
|
||||
+ /fp attack - 让假人点击鼠标左键 **(实验性)**
|
||||
+ /fp use - 让假人点击鼠标右键 **(实验性)**
|
||||
+ /fp cmd - 让假人执行他有权限执行的命令
|
||||
|
@ -21,7 +21,7 @@ public abstract class AbstractCommand {
|
||||
|
||||
protected final FakeplayerManager fakeplayerManager = FakeplayerManager.instance;
|
||||
|
||||
public static Argument<Player> targetArgument(@NotNull String nodeName) {
|
||||
public static Argument<Player> target(@NotNull String nodeName) {
|
||||
return new CustomArgument<>(new StringArgument(nodeName), info -> {
|
||||
var sender = info.sender();
|
||||
return sender.isOp()
|
||||
@ -44,7 +44,7 @@ public abstract class AbstractCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
public static Argument<List<Player>> multiTargetArgument(@NotNull String nodeName) {
|
||||
public static Argument<List<Player>> targets(@NotNull String nodeName) {
|
||||
return new CustomArgument<List<Player>, String>(new StringArgument(nodeName), info -> {
|
||||
var sender = info.sender();
|
||||
var arg = info.currentInput();
|
||||
|
@ -2,10 +2,19 @@ package io.github.hello09x.fakeplayer.command;
|
||||
|
||||
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
|
||||
import dev.jorel.commandapi.executors.CommandArguments;
|
||||
import dev.jorel.commandapi.executors.CommandExecutor;
|
||||
import io.github.hello09x.fakeplayer.entity.action.Action;
|
||||
import io.github.hello09x.fakeplayer.entity.action.ActionSetting;
|
||||
import io.github.hello09x.fakeplayer.entity.action.PlayerActionManager;
|
||||
import io.github.hello09x.fakeplayer.util.MathUtils;
|
||||
import io.github.hello09x.fakeplayer.util.Unwrapper;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
@ -19,9 +28,36 @@ public class ActionCommand extends AbstractCommand {
|
||||
|
||||
private final PlayerActionManager actionManager = PlayerActionManager.instance;
|
||||
|
||||
private static String toLocationString(@NotNull Location location) {
|
||||
return StringUtils.joinWith(", ",
|
||||
MathUtils.round(location.getX(), 0.5),
|
||||
MathUtils.round(location.getY(), 0.5),
|
||||
MathUtils.round(location.getZ(), 0.5));
|
||||
}
|
||||
|
||||
public CommandExecutor action(@NotNull Action action, @NotNull ActionSetting setting) {
|
||||
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 {
|
||||
var target = getTarget(sender, args);
|
||||
actionManager.setAction(target, action, setting);
|
||||
|
||||
String desc;
|
||||
if (setting.equals(ActionSetting.stop())) {
|
||||
desc = " 已停止";
|
||||
} else if (setting.equals(ActionSetting.once())) {
|
||||
desc = "";
|
||||
} else {
|
||||
desc = " 开始";
|
||||
}
|
||||
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName()),
|
||||
text(desc, GRAY),
|
||||
text(" "),
|
||||
text(action.name, GRAY)
|
||||
));
|
||||
}
|
||||
|
||||
public void sneak(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
|
||||
@ -36,10 +72,77 @@ public class ActionCommand extends AbstractCommand {
|
||||
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName(), WHITE),
|
||||
text("现在", GRAY),
|
||||
text(" 现在", GRAY),
|
||||
text(sneaking ? "潜行中" : "取消了潜行", GRAY)
|
||||
));
|
||||
}
|
||||
|
||||
public void lookAt(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
|
||||
var target = getTarget(sender, args);
|
||||
var location = (Location) args.get("location");
|
||||
target.lookAt(location, LookAnchor.EYES);
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName(), WHITE),
|
||||
text(" 正在看向 ", GRAY),
|
||||
text(toLocationString(location), GRAY)
|
||||
));
|
||||
}
|
||||
|
||||
public CommandExecutor look(@NotNull Direction direction) {
|
||||
return (sender, args) -> {
|
||||
var target = getTarget(sender, args);
|
||||
look(target, direction);
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName(), WHITE),
|
||||
text(" 看向 ", GRAY),
|
||||
text(switch (direction) {
|
||||
case DOWN -> "下方";
|
||||
case UP -> "上方";
|
||||
case NORTH -> "北边";
|
||||
case SOUTH -> "南边";
|
||||
case WEST -> "西边";
|
||||
case EAST -> "东边";
|
||||
}, GRAY)
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
private void look(
|
||||
@NotNull Player target,
|
||||
@NotNull Direction direction
|
||||
) {
|
||||
var player = Unwrapper.getServerPlayer(target);
|
||||
switch (direction) {
|
||||
case NORTH -> look(player, 180, 0);
|
||||
case SOUTH -> look(player, 0, 0);
|
||||
case EAST -> look(player, -90, 0);
|
||||
case WEST -> look(player, 90, 0);
|
||||
case UP -> look(player, player.getYRot(), -90);
|
||||
case DOWN -> look(player, player.getYRot(), 90);
|
||||
}
|
||||
}
|
||||
|
||||
private void look(@NotNull ServerPlayer player, float yaw, float pitch) {
|
||||
player.setYRot(yaw % 360);
|
||||
player.setXRot(MathUtils.clamp(pitch, -90, 90));
|
||||
}
|
||||
|
||||
public CommandExecutor move(float forward, float strafing) {
|
||||
return (sender, args) -> {
|
||||
var target = getTarget(sender, args);
|
||||
var player = Unwrapper.getServerPlayer(target);
|
||||
float vel = target.isSneaking() ? 0.3F : 1.0F;
|
||||
if (forward != 0.0F) {
|
||||
player.zza = vel * forward;
|
||||
}
|
||||
if (strafing != 0.0F) {
|
||||
player.xxa = vel * strafing;
|
||||
}
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName()),
|
||||
text(" 动了一下", GRAY)
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,12 +9,17 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.Component.textOfChildren;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
||||
|
||||
public class CmdCommand extends AbstractCommand {
|
||||
|
||||
public final static CmdCommand instance = new CmdCommand();
|
||||
|
||||
private static String toCommandString(@NotNull CommandResult command) {
|
||||
return "/" + command.command().getName() + String.join(" ", command.args());
|
||||
}
|
||||
|
||||
public void cmd(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
|
||||
var target = getTarget(sender, args);
|
||||
var cmd = Objects.requireNonNull((CommandResult) args.get("command"));
|
||||
@ -25,7 +30,20 @@ public class CmdCommand extends AbstractCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd.execute(target);
|
||||
if (!cmd.execute(target)) {
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName(), WHITE),
|
||||
text(" 执行命令失败: ", GRAY),
|
||||
text(toCommandString(cmd), RED),
|
||||
text(" , 请检查命令是否正确以及假人是否有权限", GRAY)
|
||||
));
|
||||
}
|
||||
|
||||
sender.sendMessage(textOfChildren(
|
||||
text(target.getName(), WHITE),
|
||||
text(" 成功执行了命令: ", GRAY),
|
||||
text(toCommandString(cmd), YELLOW)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,12 +8,15 @@ import dev.jorel.commandapi.arguments.MultiLiteralArgument;
|
||||
import dev.jorel.commandapi.executors.CommandExecutor;
|
||||
import io.github.hello09x.fakeplayer.entity.action.Action;
|
||||
import io.github.hello09x.fakeplayer.entity.action.ActionSetting;
|
||||
import net.minecraft.core.Direction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.github.hello09x.fakeplayer.command.AbstractCommand.multiTargetArgument;
|
||||
import static io.github.hello09x.fakeplayer.command.AbstractCommand.targetArgument;
|
||||
import static io.github.hello09x.fakeplayer.command.AbstractCommand.target;
|
||||
import static io.github.hello09x.fakeplayer.command.AbstractCommand.targets;
|
||||
import static io.github.hello09x.fakeplayer.command.ConfigCommand.config;
|
||||
import static io.github.hello09x.fakeplayer.command.ConfigCommand.configValue;
|
||||
|
||||
public class Commands {
|
||||
|
||||
@ -28,11 +31,11 @@ public class Commands {
|
||||
private final static String PERMISSION_CMD = "fakeplayer.cmd";
|
||||
|
||||
public static void register() {
|
||||
new CommandAPICommand("fakeplayer")
|
||||
command("fakeplayer")
|
||||
.withAliases("fp")
|
||||
.withHelp(
|
||||
"假人相关命令",
|
||||
"fakeplayer 可以用来创建一个模拟为玩家的假人, 能保持附近区块的刷新、触发怪物生成。同时还提供了一些操作命令让你控制假人的物品、动作等等。"
|
||||
"假人",
|
||||
"可以创建模拟玩家的假人, 能保持附近区块的刷新、触发怪物生成。同时还提供了一些操作命令让你控制假人的物品、动作等等。"
|
||||
)
|
||||
.withUsage(
|
||||
"§6/fp spawn §7- §f创建假人",
|
||||
@ -47,133 +50,179 @@ public class Commands {
|
||||
"§6/fp health [假人] §7- §f查看生命值",
|
||||
"§6/fp exp [假人] §7- §f查看经验值",
|
||||
"§6/fp expme [假人] §7- §f转移经验值",
|
||||
"§6/fp attack <once|continuous|interval|stop> [假人] §7- §攻击/破坏",
|
||||
"§6/fp use <once|continuous|interval|stop> [假人] §7- §f使用/交互/放置",
|
||||
"§6/fp jump <once|continuous|interval|stop> [假人] §7- §f跳跃",
|
||||
"§6/fp drop [假人] [-a|--all] §7- §f丢弃手上物品",
|
||||
"§6/fp dropinv [假人] §7- §f丢弃背包物品",
|
||||
"§6/fp sneak [假人] §7- §f开启/取消潜行",
|
||||
"§6/fp attack <once|continuous|interval|stop> [假人] §7- §f模拟鼠标左键",
|
||||
"§6/fp use <once|continuous|interval|stop> [假人] §7- §f模拟鼠标右键",
|
||||
"§6/fp cmd §7- §f让假人执行命令",
|
||||
"§6/fp look <north|south|east|west|up|down|at> §7- §f看向指定位置",
|
||||
"§6/fp move <forward|backward|left|right> §7- §f移动假人",
|
||||
"§6/fp cmd §7- §f执行命令",
|
||||
"§6/fp reload §7- §f重载配置文件"
|
||||
)
|
||||
.withSubcommands(
|
||||
new CommandAPICommand("help")
|
||||
command("help")
|
||||
.withAliases("?")
|
||||
.withOptionalArguments(new IntegerArgument("page", 1))
|
||||
.executesPlayer(HelpCommand.instance::help),
|
||||
|
||||
new CommandAPICommand("spawn")
|
||||
command("spawn")
|
||||
.withPermission(PERMISSION_SPAWN)
|
||||
.withOptionalArguments(new LocationArgument("location").withPermission(PERMISSION_SPAWN_LOCATION))
|
||||
.withOptionalArguments(location("location").withPermission(PERMISSION_SPAWN_LOCATION))
|
||||
.executes(SpawnCommand.instance::spawn),
|
||||
new CommandAPICommand("kill")
|
||||
command("kill")
|
||||
.withPermission(PERMISSION_SPAWN)
|
||||
.withOptionalArguments(multiTargetArgument("targets"))
|
||||
.withOptionalArguments(targets("targets"))
|
||||
.executes(SpawnCommand.instance::kill),
|
||||
new CommandAPICommand("list")
|
||||
command("list")
|
||||
.withPermission(PERMISSION_SPAWN)
|
||||
.withOptionalArguments(new IntegerArgument("page", 1), new IntegerArgument("size", 1))
|
||||
.withOptionalArguments(integer("page", 1), integer("size", 1))
|
||||
.executes(SpawnCommand.instance::list),
|
||||
new CommandAPICommand("distance")
|
||||
command("distance")
|
||||
.withPermission(PERMISSION_SPAWN)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executesPlayer(SpawnCommand.instance::distance),
|
||||
|
||||
new CommandAPICommand("exp")
|
||||
command("exp")
|
||||
.withPermission(PERMISSION_PROFILE)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ProfileCommand.instance::exp),
|
||||
new CommandAPICommand("health")
|
||||
command("health")
|
||||
.withPermission(PERMISSION_PROFILE)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ProfileCommand.instance::health),
|
||||
|
||||
new CommandAPICommand("tp")
|
||||
command("tp")
|
||||
.withPermission(PERMISSION_TP)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executesPlayer(TpCommand.instance::tp),
|
||||
new CommandAPICommand("tphere")
|
||||
command("tphere")
|
||||
.withPermission(PERMISSION_TP)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executesPlayer(TpCommand.instance::tphere),
|
||||
new CommandAPICommand("tps")
|
||||
command("tps")
|
||||
.withPermission(PERMISSION_TP)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executesPlayer(TpCommand.instance::tps),
|
||||
|
||||
new CommandAPICommand("config")
|
||||
command("config")
|
||||
.withSubcommands(
|
||||
new CommandAPICommand("get")
|
||||
.withArguments(ConfigCommand.configArgument("config"))
|
||||
command("get")
|
||||
.withArguments(config("config"))
|
||||
.executesPlayer(ConfigCommand.instance::getConfig),
|
||||
new CommandAPICommand("set")
|
||||
command("set")
|
||||
.withArguments(
|
||||
ConfigCommand.configArgument("config"),
|
||||
ConfigCommand.configValueArgument("config", "value"))
|
||||
config("config"),
|
||||
configValue("config", "value"))
|
||||
.executesPlayer(ConfigCommand.instance::setConfig)
|
||||
),
|
||||
new CommandAPICommand("attack")
|
||||
command("attack")
|
||||
.withPermission(PERMISSION_EXPERIMENTAL_ACTION)
|
||||
.withSubcommands(buildActionCommand(Action.ATTACK)),
|
||||
new CommandAPICommand("use")
|
||||
.withSubcommands(action(Action.ATTACK)),
|
||||
command("use")
|
||||
.withPermission(PERMISSION_EXPERIMENTAL_ACTION)
|
||||
.withSubcommands(buildActionCommand(Action.USE)),
|
||||
new CommandAPICommand("drop")
|
||||
.withSubcommands(action(Action.USE)),
|
||||
command("jump")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withSubcommands(action(Action.JUMP)),
|
||||
command("drop")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withOptionalArguments(
|
||||
targetArgument("target"),
|
||||
new MultiLiteralArgument("all", List.of("-a", "--all")))
|
||||
target("target"),
|
||||
literals("all", "-a", "--all"))
|
||||
.executes((CommandExecutor) (sender, args) -> ActionCommand.instance.action(
|
||||
sender,
|
||||
args,
|
||||
args.getOptional("all").isPresent() ? Action.DROP_STACK : Action.DROP_ITEM,
|
||||
ActionSetting.once())),
|
||||
new CommandAPICommand("dropinv")
|
||||
command("dropinv")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.executes((CommandExecutor) (sender, args) -> ActionCommand.instance.action(sender, args, Action.DROP_INVENTORY, ActionSetting.once())),
|
||||
new CommandAPICommand("sneak")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.action(Action.DROP_INVENTORY, ActionSetting.once())),
|
||||
command("sneak")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(new MultiLiteralArgument("sneaking", List.of("true", "false")))
|
||||
.withOptionalArguments(target("target"))
|
||||
.withOptionalArguments(literals("sneaking", "true", "false"))
|
||||
.executes(ActionCommand.instance::sneak),
|
||||
command("look")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withSubcommands(
|
||||
command("north")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.NORTH)),
|
||||
command("south")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.SOUTH)),
|
||||
command("west")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.WEST)),
|
||||
command("east")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.EAST)),
|
||||
command("up")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.UP)),
|
||||
command("down")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.look(Direction.DOWN)),
|
||||
command("at")
|
||||
.withArguments(new LocationArgument("location"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance::lookAt)
|
||||
),
|
||||
command("move")
|
||||
.withPermission(PERMISSION_ACTION)
|
||||
.withSubcommands(
|
||||
command("forward")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.move(1, 0)),
|
||||
command("backward")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.move(-1, 0)),
|
||||
command("left")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.move(0, 1)),
|
||||
command("right")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.move(0, -1))
|
||||
),
|
||||
|
||||
new CommandAPICommand("expme")
|
||||
command("expme")
|
||||
.withPermission(PERMISSION_EXP)
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.withOptionalArguments(target("target"))
|
||||
.executesPlayer(ExpCommand.instance::expme),
|
||||
|
||||
new CommandAPICommand("cmd")
|
||||
command("cmd")
|
||||
.withPermission(PERMISSION_CMD)
|
||||
.withArguments(
|
||||
targetArgument("target"),
|
||||
target("target"),
|
||||
new CommandArgument("command"))
|
||||
.executes(CmdCommand.instance::cmd),
|
||||
|
||||
new CommandAPICommand("reload")
|
||||
command("reload")
|
||||
.withPermission(PERMISSION_ADMIN)
|
||||
.executes(ReloadCommand.instance::reload)
|
||||
|
||||
).register();
|
||||
}
|
||||
|
||||
private static CommandAPICommand[] buildActionCommand(@NotNull Action action) {
|
||||
private static CommandAPICommand[] action(@NotNull Action action) {
|
||||
return new CommandAPICommand[]{
|
||||
new CommandAPICommand("once")
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.executes((CommandExecutor) (sender, args) -> ActionCommand.instance.action(sender, args, action, ActionSetting.once())
|
||||
command("once")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.action(action, ActionSetting.once())
|
||||
),
|
||||
new CommandAPICommand("continuous")
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.executes((CommandExecutor) (sender, args) -> ActionCommand.instance.action(sender, args, action, ActionSetting.continuous())
|
||||
command("continuous")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.action(action, ActionSetting.continuous())),
|
||||
command("stop")
|
||||
.withOptionalArguments(target("target"))
|
||||
.executes(ActionCommand.instance.action(action, ActionSetting.stop())
|
||||
),
|
||||
new CommandAPICommand("stop")
|
||||
.withOptionalArguments(targetArgument("target"))
|
||||
.executes((CommandExecutor) (sender, args) -> ActionCommand.instance.action(sender, args, action, ActionSetting.stop())
|
||||
),
|
||||
new CommandAPICommand("interval")
|
||||
command("interval")
|
||||
.withOptionalArguments(
|
||||
new IntegerArgument("interval", 1),
|
||||
targetArgument("target"))
|
||||
integer("interval", 1),
|
||||
target("target"))
|
||||
.executes((sender, args) -> {
|
||||
int interval = (int) args.getOptional("interval").orElse(1);
|
||||
ActionCommand.instance.action(sender, args, action, ActionSetting.interval(interval));
|
||||
@ -181,5 +230,21 @@ public class Commands {
|
||||
};
|
||||
}
|
||||
|
||||
private static CommandAPICommand command(@NotNull String name) {
|
||||
return new CommandAPICommand(name);
|
||||
}
|
||||
|
||||
public static IntegerArgument integer(String name, int min) {
|
||||
return new IntegerArgument(name, min);
|
||||
}
|
||||
|
||||
public static LocationArgument location(String name) {
|
||||
return new LocationArgument(name);
|
||||
}
|
||||
|
||||
public static MultiLiteralArgument literals(String name, String... literals) {
|
||||
return new MultiLiteralArgument(name, Arrays.asList(literals));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class ConfigCommand extends AbstractCommand {
|
||||
|
||||
private final UserConfigRepository repository = UserConfigRepository.instance;
|
||||
|
||||
public static Argument<Config<Object>> configArgument(String nodeName) {
|
||||
public static Argument<Config<Object>> config(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<Object> configValueArgument(String configNodeName, String nodeName) {
|
||||
public static Argument<Object> configValue(String configNodeName, String nodeName) {
|
||||
return new CustomArgument<>(new StringArgument(nodeName), info -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
var config = Objects.requireNonNull((Config<Object>) info.previousArgs().get(configNodeName));
|
||||
|
@ -26,12 +26,11 @@ public class SpawnCommand extends AbstractCommand {
|
||||
|
||||
private static String toLocationString(@NotNull Location location) {
|
||||
return location.getWorld().getName()
|
||||
+ " 世界: "
|
||||
+ ": "
|
||||
+ StringUtils.joinWith(", ",
|
||||
MathUtils.round(location.getX(), 0.5),
|
||||
MathUtils.round(location.getY(), 0.5),
|
||||
MathUtils.round(location.getZ(), 0.5));
|
||||
|
||||
}
|
||||
|
||||
public void spawn(@NotNull CommandSender sender, CommandArguments args) {
|
||||
@ -131,9 +130,9 @@ public class SpawnCommand extends AbstractCommand {
|
||||
|
||||
var distance = location1.distance(location2);
|
||||
sender.sendMessage(textOfChildren(
|
||||
text("你与 "),
|
||||
text("你与 ", GRAY),
|
||||
text(target.getName()),
|
||||
text(" 相距 "),
|
||||
text(" 相距 ", GRAY),
|
||||
text(MathUtils.round(distance, 0.5), WHITE)
|
||||
));
|
||||
}
|
||||
|
@ -130,6 +130,7 @@ public class FakePlayer {
|
||||
bukkitPlayer.setInvulnerable(invulnerable);
|
||||
bukkitPlayer.setCollidable(collidable);
|
||||
bukkitPlayer.setCanPickupItems(pickupItems);
|
||||
bukkitPlayer.getInventory().clear(); // 一些同步背包的插件可能在 JOIN 的时候恢复背包, 这个时候清空掉防止物品被复制了
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.github.hello09x.fakeplayer.entity.action;
|
||||
|
||||
import io.github.hello09x.fakeplayer.util.Tracer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@ -13,14 +13,15 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Action {
|
||||
|
||||
USE {
|
||||
USE("交互/使用/放置") {
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick(@NotNull ActionPack ap) {
|
||||
if (ap.itemUseFreeze > 0) {
|
||||
ap.itemUseFreeze--;
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
if (ap.use.freeze > 0) {
|
||||
ap.use.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ public enum Action {
|
||||
var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit);
|
||||
if (result.consumesAction()) {
|
||||
player.swing(hand);
|
||||
ap.itemUseFreeze = 3;
|
||||
ap.use.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -55,18 +56,18 @@ public enum Action {
|
||||
boolean itemFrameEmpty = (entity instanceof ItemFrame) && ((ItemFrame) entity).getItem().isEmpty();
|
||||
var pos = entityHit.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
if (entity.interactAt(player, pos, hand).consumesAction()) {
|
||||
ap.itemUseFreeze = 3;
|
||||
ap.use.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) {
|
||||
ap.itemUseFreeze = 3;
|
||||
ap.use.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handItem = player.getItemInHand(hand);
|
||||
if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) {
|
||||
ap.itemUseFreeze = 3;
|
||||
ap.use.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -74,16 +75,16 @@ public enum Action {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(@NotNull ActionPack ap) {
|
||||
ap.itemUseFreeze = 0;
|
||||
public void stop(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
ap.use.freeze = 0;
|
||||
ap.player.releaseUsingItem();
|
||||
}
|
||||
},
|
||||
|
||||
ATTACK {
|
||||
ATTACK("攻击/破坏") {
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick(@NotNull ActionPack ap) {
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
var player = ap.player;
|
||||
var hit = getTarget(player);
|
||||
switch (hit.getType()) {
|
||||
@ -96,8 +97,8 @@ public enum Action {
|
||||
return true;
|
||||
}
|
||||
case BLOCK -> {
|
||||
if (ap.blockHitFreeze > 0) {
|
||||
ap.blockHitFreeze--;
|
||||
if (ap.attack.freeze > 0) {
|
||||
ap.attack.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -109,8 +110,8 @@ public enum Action {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ap.curBlockPos != null && player.level().getBlockState(ap.curBlockPos).isAir()) {
|
||||
ap.curBlockPos = null;
|
||||
if (ap.attack.pos != null && player.level().getBlockState(ap.attack.pos).isAir()) {
|
||||
ap.attack.pos = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -124,11 +125,11 @@ public enum Action {
|
||||
player.level().getMaxBuildHeight(),
|
||||
-1
|
||||
);
|
||||
ap.blockHitFreeze = 5;
|
||||
} else if (ap.curBlockPos == null || !ap.curBlockPos.equals(pos)) {
|
||||
if (ap.curBlockPos != null) {
|
||||
ap.attack.freeze = 5;
|
||||
} else if (ap.attack.pos == null || !ap.attack.pos.equals(pos)) {
|
||||
if (ap.attack.pos != null) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
ap.curBlockPos,
|
||||
ap.attack.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxBuildHeight(),
|
||||
@ -144,20 +145,20 @@ public enum Action {
|
||||
-1
|
||||
);
|
||||
|
||||
if (!state.isAir() && ap.curBlockPgs == 0) {
|
||||
if (!state.isAir() && ap.attack.progress == 0) {
|
||||
state.attack(player.level(), pos, player);
|
||||
}
|
||||
|
||||
if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) {
|
||||
ap.curBlockPos = null;
|
||||
ap.attack.pos = null;
|
||||
broken = true;
|
||||
} else {
|
||||
ap.curBlockPos = pos;
|
||||
ap.curBlockPgs = 0;
|
||||
ap.attack.pos = pos;
|
||||
ap.attack.progress = 0;
|
||||
}
|
||||
} else {
|
||||
ap.curBlockPgs += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (ap.curBlockPgs >= 1) {
|
||||
ap.attack.progress += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (ap.attack.progress >= 1) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
STOP_DESTROY_BLOCK,
|
||||
@ -165,11 +166,11 @@ public enum Action {
|
||||
player.level().getMaxBuildHeight(),
|
||||
-1
|
||||
);
|
||||
ap.curBlockPos = null;
|
||||
ap.blockHitFreeze = 5;
|
||||
ap.attack.pos = null;
|
||||
ap.attack.freeze = 5;
|
||||
broken = true;
|
||||
}
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (ap.curBlockPgs * 10));
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (ap.attack.progress * 10));
|
||||
}
|
||||
|
||||
player.resetLastActionTime();
|
||||
@ -181,30 +182,52 @@ public enum Action {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(@NotNull ActionPack ap) {
|
||||
if (ap.curBlockPos == null) {
|
||||
@SuppressWarnings("resource")
|
||||
public void stop(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
if (ap.attack.pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var player = ap.player;
|
||||
player.level().destroyBlockProgress(-1, ap.curBlockPos, -1);
|
||||
player.level().destroyBlockProgress(-1, ap.attack.pos, -1);
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
ap.curBlockPos,
|
||||
ap.attack.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
Direction.DOWN,
|
||||
player.level().getMaxBuildHeight(),
|
||||
-1
|
||||
);
|
||||
ap.curBlockPos = null;
|
||||
ap.blockHitFreeze = 0;
|
||||
ap.curBlockPgs = 0;
|
||||
ap.attack.pos = null;
|
||||
ap.attack.freeze = 0;
|
||||
ap.attack.progress = 0;
|
||||
}
|
||||
},
|
||||
|
||||
JUMP("跳") {
|
||||
@Override
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
var player = ap.player;
|
||||
if (setting.limit == 1) {
|
||||
if (player.onGround()) {
|
||||
player.jumpFromGround();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
player.setJumping(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
ap.player.setJumping(false);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
DROP_ITEM {
|
||||
DROP_ITEM("丢弃手上物品") {
|
||||
@Override
|
||||
public boolean tick(@NotNull ActionPack ap) {
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
var player = ap.player;
|
||||
player.resetLastActionTime();
|
||||
player.drop(false);
|
||||
@ -212,9 +235,9 @@ public enum Action {
|
||||
}
|
||||
},
|
||||
|
||||
DROP_STACK {
|
||||
DROP_STACK("丢弃手上整组物品") {
|
||||
@Override
|
||||
public boolean tick(@NotNull ActionPack ap) {
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
var player = ap.player;
|
||||
player.resetLastActionTime();
|
||||
player.drop(true);
|
||||
@ -222,9 +245,9 @@ public enum Action {
|
||||
}
|
||||
},
|
||||
|
||||
DROP_INVENTORY {
|
||||
DROP_INVENTORY("丢弃背包物品") {
|
||||
@Override
|
||||
public boolean tick(@NotNull ActionPack ap) {
|
||||
public boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
var player = ap.player;
|
||||
dropInventory(player);
|
||||
return true;
|
||||
@ -232,6 +255,8 @@ public enum Action {
|
||||
};
|
||||
|
||||
|
||||
public final String name;
|
||||
|
||||
static HitResult getTarget(ServerPlayer player) {
|
||||
double reach = player.gameMode.isCreative() ? 5 : 4.5f;
|
||||
return Tracer.rayTrace(player, 1, reach, false);
|
||||
@ -244,31 +269,13 @@ public enum Action {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean tick(@NotNull ActionPack ap);
|
||||
|
||||
public void stop(@NotNull ActionPack ap) {}
|
||||
public abstract boolean tick(@NotNull ActionPack ap, @NotNull ActionSetting setting);
|
||||
|
||||
public void inactiveTick(@NotNull ActionPack ap) {
|
||||
this.stop(ap);
|
||||
}
|
||||
|
||||
public static class ActionPack {
|
||||
|
||||
public final ServerPlayer player;
|
||||
|
||||
// attack
|
||||
public BlockPos curBlockPos;
|
||||
public float curBlockPgs;
|
||||
public int blockHitFreeze;
|
||||
|
||||
|
||||
// use
|
||||
public int itemUseFreeze;
|
||||
|
||||
public ActionPack(ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
public void stop(@NotNull ActionPack ap, @NotNull ActionSetting setting) {}
|
||||
|
||||
public void inactiveTick(@NotNull ActionPack ap, @NotNull ActionSetting setting) {
|
||||
this.stop(ap, setting);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,13 +5,13 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
public class ActionManager {
|
||||
|
||||
public final Action action;
|
||||
public final Action.ActionPack actionPack;
|
||||
public final ActionPack actionPack;
|
||||
public ActionSetting setting;
|
||||
|
||||
public ActionManager(ServerPlayer player, Action action, ActionSetting setting) {
|
||||
this.action = action;
|
||||
this.setting = setting;
|
||||
this.actionPack = new Action.ActionPack(player);
|
||||
this.actionPack = new ActionPack(player);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
@ -26,7 +26,7 @@ public class ActionManager {
|
||||
return;
|
||||
}
|
||||
|
||||
var done = action.tick(this.actionPack);
|
||||
var done = action.tick(this.actionPack, this.setting);
|
||||
if (done) {
|
||||
if (setting.times > 0) {
|
||||
setting.times--;
|
||||
@ -36,11 +36,11 @@ public class ActionManager {
|
||||
}
|
||||
|
||||
public void inactiveTick() {
|
||||
action.inactiveTick(this.actionPack);
|
||||
action.inactiveTick(this.actionPack, this.setting);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
action.stop(this.actionPack);
|
||||
action.stop(this.actionPack, this.setting);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package io.github.hello09x.fakeplayer.entity.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
public class ActionPack {
|
||||
|
||||
public final ServerPlayer player;
|
||||
|
||||
public final AttackActionPack attack = new AttackActionPack();
|
||||
|
||||
public final UseActionPack use = new UseActionPack();
|
||||
|
||||
public ActionPack(ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public final static class AttackActionPack {
|
||||
public BlockPos pos;
|
||||
public float progress;
|
||||
public int freeze;
|
||||
}
|
||||
|
||||
public final static class UseActionPack {
|
||||
public int freeze;
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,17 @@
|
||||
package io.github.hello09x.fakeplayer.entity.action;
|
||||
|
||||
public class ActionSetting {
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class ActionSetting implements Cloneable {
|
||||
|
||||
/**
|
||||
* 次数
|
||||
* 总次数
|
||||
*/
|
||||
public final int limit;
|
||||
|
||||
/**
|
||||
* 剩余次数
|
||||
*/
|
||||
public int times;
|
||||
|
||||
@ -18,31 +26,38 @@ public class ActionSetting {
|
||||
public int wait;
|
||||
|
||||
public ActionSetting(int times, int interval) {
|
||||
this.times = times;
|
||||
this.interval = interval;
|
||||
this.wait = 0;
|
||||
this(times, interval, 0);
|
||||
}
|
||||
|
||||
public ActionSetting(int times, int interval, int wait) {
|
||||
this.limit = times;
|
||||
this.times = times;
|
||||
this.interval = interval;
|
||||
this.wait = wait;
|
||||
}
|
||||
|
||||
public static ActionSetting once() {
|
||||
return new ActionSetting(1, 1, 0);
|
||||
return new ActionSetting(1, 1 );
|
||||
}
|
||||
|
||||
public static ActionSetting stop() {
|
||||
return new ActionSetting(0, 1, 0);
|
||||
return new ActionSetting(0, 1);
|
||||
}
|
||||
|
||||
public static ActionSetting interval(int interval) {
|
||||
return new ActionSetting(-1, interval, 0);
|
||||
return new ActionSetting(-1, interval);
|
||||
}
|
||||
|
||||
public static ActionSetting continuous() {
|
||||
return new ActionSetting(-1, 1, 0);
|
||||
return new ActionSetting(-1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionSetting clone() {
|
||||
return new ActionSetting(
|
||||
this.times,
|
||||
this.interval,
|
||||
this.wait
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public class PlayerListeners implements Listener {
|
||||
* 拒绝假人用过的 ID 上线
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void handleUsedIdLogin(@NotNull AsyncPlayerPreLoginEvent event) {
|
||||
public void onLogin(@NotNull AsyncPlayerPreLoginEvent event) {
|
||||
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||
return;
|
||||
}
|
||||
@ -49,7 +49,7 @@ public class PlayerListeners implements Listener {
|
||||
* 死亡退出游戏
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void handlePlayerDeath(@NotNull PlayerDeathEvent event) {
|
||||
public void onDead(@NotNull PlayerDeathEvent event) {
|
||||
var player = event.getPlayer();
|
||||
if (!manager.isFake(player)) {
|
||||
return;
|
||||
@ -63,7 +63,7 @@ public class PlayerListeners implements Listener {
|
||||
* 退出游戏掉落背包
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void handlePlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||
public void onQuit(@NotNull PlayerQuitEvent event) {
|
||||
var player = event.getPlayer();
|
||||
if (!manager.isFake(player)) {
|
||||
return;
|
||||
|
@ -122,7 +122,7 @@ public class FakeplayerManager {
|
||||
|
||||
usedIdRepository.add(bukkitPlayer.getUniqueId());
|
||||
dispatchCommands(bukkitPlayer, properties.getPreparingCommands());
|
||||
performCommands(bukkitPlayer);
|
||||
performCommands(bukkitPlayer, properties.getSelfCommands());
|
||||
|
||||
bukkitPlayer.teleport(spawnAt); // 当前 tick 必须传到出生点否则无法触发区块刷新
|
||||
spawnAt.getWorld().playSound(spawnAt, Sound.ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F);
|
||||
@ -239,9 +239,7 @@ public class FakeplayerManager {
|
||||
Metadatas.NAME_SEQUENCE.get(fakePlayer).asInt()
|
||||
);
|
||||
Arrays.stream(Metadatas.values()).forEach(meta -> meta.remove(fakePlayer));
|
||||
if (properties.isDropInventoryOnQuiting()) {
|
||||
Action.dropInventory(Unwrapper.getServerPlayer(fakePlayer));
|
||||
}
|
||||
Action.dropInventory(Unwrapper.getServerPlayer(fakePlayer));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,12 +296,15 @@ public class FakeplayerManager {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void performCommands(@NotNull Player player) {
|
||||
public void performCommands(@NotNull Player player, @NotNull List<String> commands) {
|
||||
if (commands.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!isFake(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var cmd : properties.getSelfCommands()) {
|
||||
for (var cmd : commands) {
|
||||
cmd = cmd.trim();
|
||||
if (cmd.startsWith("/")) {
|
||||
cmd = cmd.substring(1);
|
||||
@ -327,7 +328,7 @@ public class FakeplayerManager {
|
||||
|
||||
var server = Bukkit.getServer();
|
||||
var sender = Bukkit.getConsoleSender();
|
||||
for (var cmd : properties.getPreparingCommands()) {
|
||||
for (var cmd : commands) {
|
||||
cmd = cmd.trim();
|
||||
if (cmd.startsWith("/")) {
|
||||
cmd = cmd.substring(1);
|
||||
|
@ -77,11 +77,6 @@ public class FakeplayerProperties extends AbstractProperties<FakeplayerPropertie
|
||||
*/
|
||||
private boolean simulateLogin;
|
||||
|
||||
/**
|
||||
* 下线时是否丢弃背包
|
||||
*/
|
||||
private boolean dropInventoryOnQuiting;
|
||||
|
||||
public FakeplayerProperties(@NotNull JavaPlugin plugin, @NotNull String version) {
|
||||
super(plugin, version);
|
||||
}
|
||||
@ -102,7 +97,6 @@ public class FakeplayerProperties extends AbstractProperties<FakeplayerPropertie
|
||||
this.destroyCommands = file.getStringList("destroy-commands");
|
||||
this.nameTemplate = file.getString("name-template", "");
|
||||
this.simulateLogin = file.getBoolean("simulate-login", false);
|
||||
this.dropInventoryOnQuiting = file.getBoolean("drop-inventory-on-quiting", true);
|
||||
|
||||
if (this.nameTemplate.startsWith("-")) {
|
||||
log.warning("假人名称模版不能以 - 开头, 该配置不会生效: " + this.nameTemplate);
|
||||
|
@ -10,5 +10,9 @@ public class MathUtils {
|
||||
return Math.floor(num / base) * base;
|
||||
}
|
||||
|
||||
public static float clamp(float value, float min, float max) {
|
||||
return value < min ? min : Math.min(value, max);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,16 +24,6 @@ name-template: ''
|
||||
# 如果玩家只是切换服务器, 那么不会触发跟随下线
|
||||
follow-quiting: true
|
||||
|
||||
# 下线时是否丢弃背包
|
||||
# 如果服务器有背包同步之类的插件,请验证是否会有复制 bug
|
||||
# 这个 bug 原因是先保存了背包再丢弃背包的物品导致地上和背包各一份
|
||||
# 验证方法如下:
|
||||
# 1. 创建假人,并给他一点东西
|
||||
# 2. kick 掉假人, 此时他会丢弃背包的物品
|
||||
# 3. 重新创建假人, 看看他背包的物品是否多了一份
|
||||
drop-inventory-on-quiting: true
|
||||
|
||||
# 是否检测 IP
|
||||
# 如果启用, 则一个 IP 只能创建 `maximum` 个假人
|
||||
# 能够避免玩家开小号疯狂创建假人
|
||||
detect-ip: true
|
||||
@ -75,7 +65,7 @@ destroy-commands:
|
||||
|
||||
# 自执行命令
|
||||
# 假人在诞生时会以自己的身份按顺序执行命令
|
||||
# 你可以在这里做一些 /register 之类的命令
|
||||
# 你可以在这里做添加 /register 和 /login 命令来防止 `AuthMe` 等插件踢掉超时未登陆的玩家
|
||||
self-commands:
|
||||
- ''
|
||||
- ''
|
||||
|
@ -32,7 +32,7 @@ permissions:
|
||||
default: op
|
||||
|
||||
fakeplayer.action:
|
||||
description: '拥有 drop, dropinv, sneak 命令权限'
|
||||
description: '拥有 drop, dropinv, sneak, look, move, jump 命令权限'
|
||||
default: op
|
||||
|
||||
fakeplayer.experimental.action:
|
||||
|
Loading…
Reference in New Issue
Block a user