添加 invsee, sleep, wakeup 命令

This commit is contained in:
tanyaofei 2023-11-06 16:50:11 +08:00
parent d7f01bc91e
commit 6eb3b1741e
20 changed files with 497 additions and 26 deletions

View File

@ -11,6 +11,16 @@
1. 这个假人有点"真", 与真实玩家一致, 可以保持区块的刷新和怪物生成, 距离取决于服务器设置的模拟距离 1. 这个假人有点"真", 与真实玩家一致, 可以保持区块的刷新和怪物生成, 距离取决于服务器设置的模拟距离
2. 假人的原生数据档案、成就数据不会存档,但第三方的档案还会存在 2. 假人的原生数据档案、成就数据不会存档,但第三方的档案还会存在
## 前置
### 必须前置:
- CommandAPI
### 可选前置:
- OpenInv: 有这个插件则玩家打开假人背包时可以操作编辑, 否则只能查看
## 命令 ## 命令
+ `/fp spawn [名称] [世界] [位置]` - 创建假人 + `/fp spawn [名称] [世界] [位置]` - 创建假人
@ -22,10 +32,12 @@
+ `/fp tps` - 与假人交换位置 + `/fp tps` - 与假人交换位置
+ `/fp config get <配置项>` - 查看配置项 + `/fp config get <配置项>` - 查看配置项
+ `/fp config set <配置项> <配置值>` - 设置配置项 + `/fp config set <配置项> <配置值>` - 设置配置项
+ `/fp config list` - 查看所有配置项
+ `/fp health` - 查看生命值 + `/fp health` - 查看生命值
+ `/fp exp` - 查看经验值 + `/fp exp` - 查看经验值
+ `/fp expme` - 转移经验值 + `/fp expme` - 转移经验值
+ `/fp attack (once | continuous | interval | stop)` - 攻击/破坏 + `/fp attack (once | continuous | interval | stop)` - 攻击
+ `/fp mine (once | continuous | interval | stop)` - 挖掘
+ `/fp use (once | continuous | interval | stop)` - 使用/交互/放置 + `/fp use (once | continuous | interval | stop)` - 使用/交互/放置
+ `/fp jump (once | continuous | interval | stop)` - 跳跃 + `/fp jump (once | continuous | interval | stop)` - 跳跃
+ `/fp drop [-a|--all]` - 丢弃手上物品 + `/fp drop [-a|--all]` - 丢弃手上物品
@ -34,6 +46,10 @@
+ `/fp turn (left | right | back | to)` - 转身到指定位置 + `/fp turn (left | right | back | to)` - 转身到指定位置
+ `/fp move (forward | backward | left | right)` - 移动假人 + `/fp move (forward | backward | left | right)` - 移动假人
+ `/fp ride (anything | vehicle | target | stop)` - 骑乘 + `/fp ride (anything | vehicle | target | stop)` - 骑乘
+ `/fp sleep` - 睡觉
+ `/fp wakup` - 起床
+ `/fp skin` - 拷贝皮肤
+ `/fp invsee` - 查看背包 _(有 OpenInv 前置则可以编辑)_
+ `/fp cmd <假人> <命令>` - 执行命令 + `/fp cmd <假人> <命令>` - 执行命令
+ `/fp reload` - 重载配置文件 + `/fp reload` - 重载配置文件
@ -43,18 +59,18 @@
**_默认所有权限是 op 拥有请通过权限管理插件来分配_** **_默认所有权限是 op 拥有请通过权限管理插件来分配_**
| 节点 | 指令 | | 节点 | 指令 |
|---------------------------|------------------------------------------------------------------| |---------------------------|-----------------------------------------------------------------------------------------------|
| fakeplayer.spawn | `spawn`, `list`, `kill`, `distance`, `dropinv`, `drop` | | fakeplayer.spawn | `spawn`, `list`, `kill`, `distance`, `dropinv`, `drop`, `skin`, `invsee` |
| fakeplayer.spawn.location | `spawn` 可以指定出生点 | | fakeplayer.spawn.location | `spawn` 可以指定出生点 |
| fakeplayer.spawn.name | `spawn` 可以自定义名称 | | fakeplayer.spawn.name | `spawn` 可以自定义名称 |
| fakeplayer.tp | `tp`, `tps`, `tphere` | | fakeplayer.tp | `tp`, `tps`, `tphere` |
| fakeplayer.profile | `exp`, `health` | | fakeplayer.profile | `exp`, `health` |
| fakeplayer.exp | `expme` | | fakeplayer.exp | `expme` |
| fakeplayer.action | `sneak`, `turn`, `jump`, `look`, `move`, `attack`, `use`, `ride` | | fakeplayer.action | `sneak`, `turn`, `jump`, `look`, `move`, `attack`, `use`, `ride`, `refill`, `sleep`, `wakeup` |
| fakeplayer.cmd | `cmd` | | fakeplayer.cmd | `cmd` |
| fakeplayer.admin | `reload` | | fakeplayer.admin | `reload` |
| 无 | `config` | | 无 | `config` |
## 玩家个性化配置 ## 玩家个性化配置

View File

@ -51,6 +51,11 @@
<artifactId>commandapi-bukkit-core</artifactId> <artifactId>commandapi-bukkit-core</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.jikoo.OpenInv</groupId>
<artifactId>openinvapi</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -7,6 +7,7 @@ import io.github.hello09x.fakeplayer.api.spi.VersionSupport;
import io.github.hello09x.fakeplayer.core.command.CommandRegistry; import io.github.hello09x.fakeplayer.core.command.CommandRegistry;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig; import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.listener.PlayerListeners; import io.github.hello09x.fakeplayer.core.listener.PlayerListeners;
import io.github.hello09x.fakeplayer.core.listener.RefillListener;
import io.github.hello09x.fakeplayer.core.manager.WildFakeplayerManager; import io.github.hello09x.fakeplayer.core.manager.WildFakeplayerManager;
import io.github.hello09x.fakeplayer.core.util.update.UpdateChecker; import io.github.hello09x.fakeplayer.core.util.update.UpdateChecker;
import lombok.Getter; import lombok.Getter;
@ -48,6 +49,7 @@ public final class Main extends RegistrablePlugin implements I18nSupported {
{ {
getServer().getPluginManager().registerEvents(PlayerListeners.instance, this); getServer().getPluginManager().registerEvents(PlayerListeners.instance, this);
getServer().getPluginManager().registerEvents(RefillListener.instance, this);
} }
if (FakeplayerConfig.instance.isCheckForUpdates()) { if (FakeplayerConfig.instance.isCheckForUpdates()) {

View File

@ -136,8 +136,8 @@ public class ActionCommand extends AbstractCommand {
case SOUTH -> look(target, 0, 0); case SOUTH -> look(target, 0, 0);
case EAST -> look(target, -90, 0); case EAST -> look(target, -90, 0);
case WEST -> look(target, 90, 0); case WEST -> look(target, 90, 0);
case UP -> look(target, target.getYaw(), -90); case UP -> look(target, target.getLocation().getYaw(), -90);
case DOWN -> look(target, target.getYaw(), 90); case DOWN -> look(target, target.getLocation().getYaw(), 90);
} }
} }
@ -187,7 +187,8 @@ public class ActionCommand extends AbstractCommand {
} }
private void turn(@NotNull Player player, float yaw, float pitch) { private void turn(@NotNull Player player, float yaw, float pitch) {
look(player, player.getYaw() + yaw, player.getPitch() + pitch); var pos = player.getLocation();
look(player, pos.getYaw() + yaw, pos.getPitch() + pitch);
} }

View File

@ -53,6 +53,9 @@ public class CommandRegistry {
Usage.of("drop", i18n.asString("fakeplayer.command.drop.description"), Permission.spawn), Usage.of("drop", i18n.asString("fakeplayer.command.drop.description"), Permission.spawn),
Usage.of("dropinv", i18n.asString("fakeplayer.command.dropinv.description"), Permission.spawn), Usage.of("dropinv", i18n.asString("fakeplayer.command.dropinv.description"), Permission.spawn),
Usage.of("skin", i18n.asString("fakeplayer.command.skin.description"), Permission.spawn), Usage.of("skin", i18n.asString("fakeplayer.command.skin.description"), Permission.spawn),
Usage.of("invsee", i18n.asString("fakeplayer.command.invsee.description"), Permission.spawn),
Usage.of("sleep", i18n.asString("fakeplayer.command.sleep.description"), Permission.spawn),
Usage.of("wakeup", i18n.asString("fakeplayer.command.wakeup.description"), Permission.spawn),
Usage.of("tp", i18n.asString("fakeplayer.command.tp.description"), Permission.tp), Usage.of("tp", i18n.asString("fakeplayer.command.tp.description"), Permission.tp),
Usage.of("tphere", i18n.asString("fakeplayer.command.tphere.description"), Permission.tp), Usage.of("tphere", i18n.asString("fakeplayer.command.tphere.description"), Permission.tp),
Usage.of("tps", i18n.asString("fakeplayer.command.tps.description"), Permission.tp), Usage.of("tps", i18n.asString("fakeplayer.command.tps.description"), Permission.tp),
@ -64,13 +67,14 @@ public class CommandRegistry {
Usage.of("attack", i18n.asString("fakeplayer.command.attack.description"), Permission.action), Usage.of("attack", i18n.asString("fakeplayer.command.attack.description"), Permission.action),
Usage.of("mine", i18n.asString("fakeplayer.command.mine.description"), Permission.action), Usage.of("mine", i18n.asString("fakeplayer.command.mine.description"), Permission.action),
Usage.of("use", i18n.asString("fakeplayer.command.use.description"), Permission.action), Usage.of("use", i18n.asString("fakeplayer.command.use.description"), Permission.action),
Usage.of("refill", i18n.asString("fakeplayer.command.refill.description"), Permission.action),
Usage.of("jump", i18n.asString("fakeplayer.command.jump.description"), Permission.action), Usage.of("jump", i18n.asString("fakeplayer.command.jump.description"), Permission.action),
Usage.of("look", i18n.asString("fakeplayer.command.look.description"), Permission.action), Usage.of("look", i18n.asString("fakeplayer.command.look.description"), Permission.action),
Usage.of("turn", i18n.asString("fakeplayer.command.turn.description"), Permission.action), Usage.of("turn", i18n.asString("fakeplayer.command.turn.description"), Permission.action),
Usage.of("move", i18n.asString("fakeplayer.command.move.description"), Permission.action), Usage.of("move", i18n.asString("fakeplayer.command.move.description"), Permission.action),
Usage.of("ride", i18n.asString("fakeplayer.command.ride.description"), Permission.action), Usage.of("ride", i18n.asString("fakeplayer.command.ride.description"), Permission.action),
Usage.of("sneak", i18n.asString("fakeplayer.command.sneak.description"), Permission.action), Usage.of("sneak", i18n.asString("fakeplayer.command.sneak.description"), Permission.action),
Usage.of("swap", i18n.asString("fakeplayer.command.swap.description"), Permission.spawn), Usage.of("swap", i18n.asString("fakeplayer.command.swap.description"), Permission.action),
Usage.of("cmd", i18n.asString("fakeplayer.command.cmd.description"), Permission.cmd), Usage.of("cmd", i18n.asString("fakeplayer.command.cmd.description"), Permission.cmd),
Usage.of("reload", i18n.asString("fakeplayer.command.reload.description"), Permission.admin) Usage.of("reload", i18n.asString("fakeplayer.command.reload.description"), Permission.admin)
), ),
@ -115,6 +119,10 @@ public class CommandRegistry {
.withArguments(offlinePlayer("player")) .withArguments(offlinePlayer("player"))
.withOptionalArguments(fakeplayer("name")) .withOptionalArguments(fakeplayer("name"))
.executes(SkinCommand.instance::skin), .executes(SkinCommand.instance::skin),
command("invsee")
.withPermission(Permission.spawn)
.withOptionalArguments(fakeplayer("name"))
.executesPlayer(InvseeCommand.instance::invsee),
command("exp") command("exp")
.withPermission(Permission.profile) .withPermission(Permission.profile)
@ -147,7 +155,9 @@ public class CommandRegistry {
.withArguments( .withArguments(
config("option"), config("option"),
configValue("option", "value")) configValue("option", "value"))
.executesPlayer(ConfigCommand.instance::setConfig) .executesPlayer(ConfigCommand.instance::setConfig),
command("list")
.executesPlayer(ConfigCommand.instance::listConfig)
), ),
command("attack") command("attack")
@ -287,6 +297,21 @@ public class CommandRegistry {
.withPermission(Permission.action) .withPermission(Permission.action)
.withOptionalArguments(fakeplayer("name")) .withOptionalArguments(fakeplayer("name"))
.executes(ActionCommand.instance::swap), .executes(ActionCommand.instance::swap),
command("refill")
.withPermission(Permission.action)
.withOptionalArguments(
literals("enabled", List.of("true", "false")),
fakeplayer("name")
)
.executes(RefillCommand.instance::refill),
command("sleep")
.withPermission(Permission.action)
.withOptionalArguments(fakeplayer("name"))
.executes(SleepCommand.instance::sleep),
command("wakeup")
.withPermission(Permission.action)
.withOptionalArguments(fakeplayer("name"))
.executes(SleepCommand.instance::wakeup),
command("expme") command("expme")
.withPermission(Permission.exp) .withPermission(Permission.exp)

View File

@ -1,16 +1,21 @@
package io.github.hello09x.fakeplayer.core.command; package io.github.hello09x.fakeplayer.core.command;
import dev.jorel.commandapi.executors.CommandArguments; import dev.jorel.commandapi.executors.CommandArguments;
import io.github.hello09x.bedrock.util.Components;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.repository.UserConfigRepository; import io.github.hello09x.fakeplayer.core.repository.UserConfigRepository;
import io.github.hello09x.fakeplayer.core.repository.model.Config; import io.github.hello09x.fakeplayer.core.repository.model.Config;
import io.github.hello09x.fakeplayer.core.repository.model.Configs;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.Component.textOfChildren;
import static net.kyori.adventure.text.format.NamedTextColor.*; import static net.kyori.adventure.text.format.NamedTextColor.*;
public class ConfigCommand extends AbstractCommand { public class ConfigCommand extends AbstractCommand {
@ -44,5 +49,22 @@ public class ConfigCommand extends AbstractCommand {
)); ));
} }
public void listConfig(@NotNull Player sender, @NotNull CommandArguments args) {
var uuid = sender.getUniqueId();
CompletableFuture.runAsync(() -> {
var components = Arrays.stream(Configs.values()).map(config -> {
var value = String.valueOf(repository.selectOrDefault(uuid, config));
return textOfChildren(
i18n.translate(config, GOLD),
text(": ", GRAY),
text(value, WHITE)
);
}).toList();
var message = Components.join(components, newline());
Bukkit.getScheduler().runTask(Main.getInstance(), () -> sender.sendMessage(message));
});
}
} }

View File

@ -0,0 +1,23 @@
package io.github.hello09x.fakeplayer.core.command;
import dev.jorel.commandapi.CommandAPI;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class InvseeCommand extends AbstractCommand {
public final static InvseeCommand instance = new InvseeCommand();
public void invsee(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
if (!Objects.equals(sender.getLocation().getWorld(), target.getLocation().getWorld())) {
throw CommandAPI.failWithString(i18n.asString("fakeplayer.command.invsee.error.not-the-same-world"));
}
fakeplayerManager.openInventory(sender, target);
}
}

View File

@ -0,0 +1,33 @@
package io.github.hello09x.fakeplayer.core.command;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.*;
public class RefillCommand extends AbstractCommand {
public final static RefillCommand instance = new RefillCommand();
public void refill(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
var refillable = args.getOptional("enabled").map(value -> Boolean.parseBoolean((String) value)).orElse(null);
if (refillable == null) {
refillable = !fakeplayerManager.isRefillable(target);
}
fakeplayerManager.setRefillable(target, refillable);
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.refill.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE)),
Placeholder.component("status", refillable
? i18n.translate("fakeplayer.generic.enabled", DARK_GREEN)
: i18n.translate("fakeplayer.generic.disabled", GRAY))
)
);
}
}

View File

@ -0,0 +1,44 @@
package io.github.hello09x.fakeplayer.core.command;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import io.github.hello09x.bedrock.util.Blocks;
import org.bukkit.block.data.type.Bed;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class SleepCommand extends AbstractCommand {
public final static SleepCommand instance = new SleepCommand();
public void sleep(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
var bed = Blocks.getNearbyBlock(target.getLocation(), 4, block -> {
if (block.getType().data != Bed.class) {
return false;
}
var data = (Bed) block.getBlockData();
if (data.isOccupied()) {
return false;
}
if (data.getPart() == Bed.Part.FOOT) {
return false;
}
return true;
});
if (bed == null) {
return;
}
target.sleep(bed.getLocation(), false);
}
public void wakeup(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
if (!target.isSleeping()) {
return;
}
target.wakeup(true);
}
}

View File

@ -8,6 +8,7 @@ import io.github.hello09x.fakeplayer.api.action.ActionType;
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer; import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
import io.github.hello09x.fakeplayer.core.Main; import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig; import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
import io.github.hello09x.fakeplayer.core.manager.action.ActionManager; import io.github.hello09x.fakeplayer.core.manager.action.ActionManager;
import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName; import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName;
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator; import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
@ -145,6 +146,9 @@ public class FakePlayer {
if (option.skin() && this.creator instanceof Player playerCreator) { if (option.skin() && this.creator instanceof Player playerCreator) {
handle.copyTexture(playerCreator); handle.copyTexture(playerCreator);
} }
if (option.refillable()) {
FakeplayerManager.instance.setRefillable(player, true);
}
var network = Main.getVersionSupport().network(); var network = Main.getVersionSupport().network();
network.bindEmptyServerGamePacketListener(Bukkit.getServer(), this.player, address); network.bindEmptyServerGamePacketListener(Bukkit.getServer(), this.player, address);

View File

@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
* @param collidable 是否开启碰撞 * @param collidable 是否开启碰撞
* @param lookAtEntity 是否看向附近实体 * @param lookAtEntity 是否看向附近实体
* @param pickupItems 是否拾取物品 * @param pickupItems 是否拾取物品
* @param refillable 是否自动填装
*/ */
public record SpawnOption( public record SpawnOption(
@NotNull @NotNull
@ -22,7 +23,9 @@ public record SpawnOption(
boolean pickupItems, boolean pickupItems,
boolean skin boolean skin,
boolean refillable
) { ) {
} }

View File

@ -9,11 +9,15 @@ import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator; import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -58,6 +62,19 @@ public class PlayerListeners implements Listener {
} }
} }
/**
* 由于查看假人背包时看不到盔甲栏, 且在将物品从假人背包移动到玩家背包时候有概率会将物品放置到假人的盔甲栏而找不到了, 因此禁止玩家操作假人背包
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onClickFakePlayerInventory(@NotNull InventoryClickEvent event) {
var top = event.getView().getTopInventory();
if (top.getType() == InventoryType.PLAYER && (top.getHolder() instanceof Player player && manager.isFake(player))) {
if (event.getView().getTitle().startsWith("*")) {
event.setCancelled(true);
}
}
}
/** /**
* 死亡退出游戏 * 死亡退出游戏
*/ */
@ -96,4 +113,16 @@ public class PlayerListeners implements Listener {
} }
} }
/**
* 右键假人打开其背包
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void invsee(@NotNull PlayerInteractAtEntityEvent event) {
if (!(event.getRightClicked() instanceof Player target) || !manager.isFake(target)) {
return;
}
manager.openInventory(event.getPlayer(), target);
}
} }

View File

@ -0,0 +1,124 @@
package io.github.hello09x.fakeplayer.core.listener;
import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerItemBreakEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class RefillListener implements Listener {
public final static RefillListener instance = new RefillListener();
private final FakeplayerManager manager = FakeplayerManager.instance;
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onItemUse(@NotNull PlayerItemConsumeEvent event) {
var player = event.getPlayer();
if (!manager.isRefillable(player)) {
return;
}
var slot = event.getHand();
var item = player.getInventory().getItem(slot);
if (item.getAmount() != 1) {
return;
}
this.refillLater(player, slot, item);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
var player = event.getPlayer();
if (!manager.isRefillable(player)) {
return;
}
var slot = event.getHand();
var item = player.getInventory().getItem(slot);
if (item.getAmount() != 1) {
return;
}
this.refillLater(player, slot, item);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onItemBreak(@NotNull PlayerItemBreakEvent event) {
var player = event.getPlayer();
if (!manager.isRefillable(player)) {
return;
}
var item = event.getBrokenItem();
var slot = getHoldingHand(player, item);
if (slot == null) {
return;
}
this.refillLater(player, slot, item);
}
/**
* 发射投掷物, 如扔喷溅型药水
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onProjectileLaunch(@NotNull PlayerLaunchProjectileEvent event) {
var player = event.getPlayer();
if (!manager.isRefillable(event.getPlayer())) {
return;
}
var item = event.getItemStack();
if (item.getAmount() != 1) {
return;
}
var slot = getHoldingHand(player, item);
if (slot == null) {
return;
}
this.refillLater(player, slot, item);
}
public void refillLater(@NotNull Player player, @NotNull EquipmentSlot slot, @NotNull ItemStack item) {
var requires = item.clone();
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
var inv = player.getInventory();
var current = inv.getItem(slot);
for (int i = inv.getSize(); i >= 0; i--) {
var replacement = inv.getItem(i);
if (replacement != null && replacement.isSimilar(requires)) {
inv.setItem(slot, replacement);
inv.setItem(i, current);
break;
}
}
}, 1);
}
private @Nullable EquipmentSlot getHoldingHand(@NotNull Player player, @NotNull ItemStack item) {
var inv = player.getInventory();
if (item.equals(inv.getItemInMainHand())) {
return EquipmentSlot.HAND;
} else if (item.equals(inv.getItemInOffHand())) {
return EquipmentSlot.OFF_HAND;
} else {
return null;
}
}
}

View File

@ -3,6 +3,7 @@ package io.github.hello09x.fakeplayer.core.manager;
import io.github.hello09x.bedrock.command.MessageException; import io.github.hello09x.bedrock.command.MessageException;
import io.github.hello09x.bedrock.i18n.I18n; import io.github.hello09x.bedrock.i18n.I18n;
import io.github.hello09x.bedrock.task.Tasks; import io.github.hello09x.bedrock.task.Tasks;
import io.github.hello09x.bedrock.util.Components;
import io.github.hello09x.fakeplayer.api.action.ActionSetting; import io.github.hello09x.fakeplayer.api.action.ActionSetting;
import io.github.hello09x.fakeplayer.api.action.ActionType; import io.github.hello09x.fakeplayer.api.action.ActionType;
import io.github.hello09x.fakeplayer.core.Main; import io.github.hello09x.fakeplayer.core.Main;
@ -16,13 +17,17 @@ import io.github.hello09x.fakeplayer.core.manager.naming.exception.IllegalCustom
import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository; import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
import io.github.hello09x.fakeplayer.core.repository.UserConfigRepository; import io.github.hello09x.fakeplayer.core.repository.UserConfigRepository;
import io.github.hello09x.fakeplayer.core.repository.model.Configs; import io.github.hello09x.fakeplayer.core.repository.model.Configs;
import io.github.hello09x.fakeplayer.core.softdepend.OpenInvDepend;
import io.github.hello09x.fakeplayer.core.util.AddressUtils; import io.github.hello09x.fakeplayer.core.util.AddressUtils;
import io.github.hello09x.fakeplayer.core.util.Commands; import io.github.hello09x.fakeplayer.core.util.Commands;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -47,6 +52,8 @@ public class FakeplayerManager {
private final static Logger log = Main.getInstance().getLogger(); private final static Logger log = Main.getInstance().getLogger();
private final static MiniMessage miniMessage = MiniMessage.miniMessage();
private final FakeplayerConfig config = FakeplayerConfig.instance; private final FakeplayerConfig config = FakeplayerConfig.instance;
private final UsedIdRepository usedIdRepository = UsedIdRepository.instance; private final UsedIdRepository usedIdRepository = UsedIdRepository.instance;
@ -59,6 +66,8 @@ public class FakeplayerManager {
private final I18n i18n = Main.i18n(); private final I18n i18n = Main.i18n();
private final OpenInvDepend openInvDepend = OpenInvDepend.instance;
private FakeplayerManager() { private FakeplayerManager() {
var timer = Executors.newSingleThreadScheduledExecutor(); var timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(() -> { timer.scheduleAtFixedRate(() -> {
@ -113,7 +122,8 @@ public class FakeplayerManager {
lookAtEntity = Configs.look_at_entity.defaultValue(), lookAtEntity = Configs.look_at_entity.defaultValue(),
collidable = Configs.collidable.defaultValue(), collidable = Configs.collidable.defaultValue(),
pickupItems = Configs.pickup_items.defaultValue(), pickupItems = Configs.pickup_items.defaultValue(),
skin = Configs.skin.defaultValue(); skin = Configs.skin.defaultValue(),
refillable = Configs.refillable.defaultValue();
if (creator instanceof Player p) { if (creator instanceof Player p) {
var creatorId = p.getUniqueId(); var creatorId = p.getUniqueId();
@ -122,6 +132,7 @@ public class FakeplayerManager {
collidable = userConfigRepository.selectOrDefault(creatorId, Configs.collidable); collidable = userConfigRepository.selectOrDefault(creatorId, Configs.collidable);
pickupItems = userConfigRepository.selectOrDefault(creatorId, Configs.pickup_items); pickupItems = userConfigRepository.selectOrDefault(creatorId, Configs.pickup_items);
skin = userConfigRepository.selectOrDefault(creatorId, Configs.skin); skin = userConfigRepository.selectOrDefault(creatorId, Configs.skin);
refillable = userConfigRepository.selectOrDefault(creatorId, Configs.refillable);
} }
return new SpawnOption( return new SpawnOption(
@ -130,7 +141,8 @@ public class FakeplayerManager {
collidable, collidable,
lookAtEntity, lookAtEntity,
pickupItems, pickupItems,
skin skin,
refillable
); );
}) })
.thenApplyAsync(option -> fp.spawnAsync(option).join()) .thenApplyAsync(option -> fp.spawnAsync(option).join())
@ -289,6 +301,22 @@ public class FakeplayerManager {
.count(); .count();
} }
public void setRefillable(@NotNull Player player, boolean refillable) {
if (!this.isFake(player)) {
return;
}
if (!refillable) {
player.removeMetadata("fakeplayer:refillable", Main.getInstance());
} else {
player.setMetadata("fakeplayer:refillable", new FixedMetadataValue(Main.getInstance(), true));
}
}
public boolean isRefillable(@NotNull Player player) {
return player.hasMetadata("fakeplayer:refillable");
}
/** /**
* 以假人身份执行命令 * 以假人身份执行命令
* *
@ -339,6 +367,21 @@ public class FakeplayerManager {
} }
} }
public boolean openInventory(@NotNull Player creator, @NotNull Player player) {
var fake = this.playerList.getByName(player.getName());
if (fake == null) {
return false;
}
if (!creator.isOp() && !fake.getCreator().equals(creator)) {
return false;
}
if (!openInvDepend.openInventory(creator, player)) {
this.openInventoryDefault(creator, player);
}
return true;
}
private void checkLimit(@NotNull CommandSender creator) throws MessageException { private void checkLimit(@NotNull CommandSender creator) throws MessageException {
if (creator.isOp()) { if (creator.isOp()) {
return; return;
@ -357,4 +400,14 @@ public class FakeplayerManager {
} }
} }
private void openInventoryDefault(@NotNull Player player, @NotNull Player target) {
var view = player.openInventory(target.getInventory());
if (view != null) {
view.setTitle("* " + Components.asString(miniMessage.deserialize(
i18n.asString("fakeplayer.manager.inventory.title"),
Placeholder.component("name", text(target.getName()))
)));
}
}
} }

View File

@ -64,6 +64,14 @@ public interface Configs {
Boolean::valueOf Boolean::valueOf
); );
Config<Boolean> refillable = build(
"refillable",
"fakeplayer.config.refillable",
false,
List.of("true", "false"),
Boolean::valueOf
);
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private static <T> Config<T> build( private static <T> Config<T> build(
@NotNull String name, @NotNull String name,

View File

@ -0,0 +1,45 @@
package io.github.hello09x.fakeplayer.core.softdepend;
import com.lishid.openinv.IOpenInv;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class OpenInvDepend {
public final static OpenInvDepend instance = new OpenInvDepend();
private Bridge bridge = null;
public OpenInvDepend() {
try {
if (Bukkit.getPluginManager().getPlugin("OpenInv") != null) {
bridge = new Bridge();
}
} catch (Throwable ignored) {
}
}
public boolean openInventory(@NotNull Player player, @NotNull Player target) {
if (bridge == null) {
return false;
}
try {
bridge.openInventory(player, target);
} catch (Throwable e) {
return false;
}
return true;
}
private final static class Bridge {
private IOpenInv iOpenInv = (IOpenInv) Bukkit.getPluginManager().getPlugin("OpenInv");
public void openInventory(@NotNull Player player, @NotNull Player target) throws Throwable {
iOpenInv.openInventory(player, iOpenInv.getSpecialInventory(target, true));
}
}
}

View File

@ -120,4 +120,14 @@ fakeplayer.command.config.set.error.invalid-value=Unknown value
fakeplayer.command.generic.error.non-fake-player=You haven't spawn fake player yet fakeplayer.command.generic.error.non-fake-player=You haven't spawn fake player yet
fakeplayer.command.skin.description=Copy skin from a player who has played before fakeplayer.command.skin.description=Copy skin from a player who has played before
fakeplayer.command.skin.error.too-many-operations=You did this too frequently, you can't copy skin of offline players for now, try online players instaed~ fakeplayer.command.skin.error.too-many-operations=You did this too frequently, you can't copy skin of offline players for now, try online players instaed~
fakeplayer.command.refill.description=Config whether auto replacing their broken tools, or items
fakeplayer.manager.inventory.title=<name>'s Inventory
fakeplayer.command.invsee.error.not-the-same-world=You are not in the same world
fakeplayer.command.refill.success=<name> refillable was set to <status>
fakeplayer.generic.enabled=enabled
fakeplayer.generic.disabled=disabled
fakeplayer.config.refillable=Auto-Refill
fakeplayer.command.invsee.description=Open inventory
fakeplayer.command.sleep.description=Sleep
fakeplayer.command.wakeup.description=Wakup

View File

@ -1,12 +1,12 @@
fakeplayer.command.attack.description=\u653B\u51FB fakeplayer.command.attack.description=\u653B\u51FB
fakeplayer.command.config.get.description=\u67E5\u770B\u914D\u7F6E fakeplayer.command.config.get.description=\u67E5\u770B\u914D\u7F6E
fakeplayer.command.config.set.description=\u8BBE\u7F6E\u914D\u7F6E\u9879 fakeplayer.command.config.set.description=\u8BBE\u7F6E\u914D\u7F6E\u9879
fakeplayer.command.distance.description=\u67E5\u770B\u4E0E\u5BB6\u4EBA\u7684\u8DDD\u79BB fakeplayer.command.distance.description=\u67E5\u770B\u8DDD\u79BB
fakeplayer.command.drop.description=\u4E22\u5F03\u624B\u4E0A\u7269\u54C1 fakeplayer.command.drop.description=\u4E22\u5F03\u624B\u4E0A\u7269\u54C1
fakeplayer.command.dropinv.description=\u4E22\u5F03\u80CC\u5305\u7269\u54C1 fakeplayer.command.dropinv.description=\u4E22\u5F03\u80CC\u5305\u7269\u54C1
fakeplayer.command.exp.description=\u67E5\u770B\u7ECF\u9A8C\u503C fakeplayer.command.exp.description=\u67E5\u770B\u7ECF\u9A8C\u503C
fakeplayer.command.expme.description=\u8F6C\u79FB\u7ECF\u9A8C\u503C fakeplayer.command.expme.description=\u8F6C\u79FB\u7ECF\u9A8C\u503C
fakeplayer.command.health.description=\u67E5\u770B\u5047\u4EBA\u751F\u547D\u503C fakeplayer.command.health.description=\u67E5\u770B\u751F\u547D\u503C
fakeplayer.command.jump.description=\u8DF3\u8DC3 fakeplayer.command.jump.description=\u8DF3\u8DC3
fakeplayer.command.kill.description=\u79FB\u9664\u5047\u4EBA fakeplayer.command.kill.description=\u79FB\u9664\u5047\u4EBA
fakeplayer.command.list.description=\u67E5\u770B\u6240\u6709\u5047\u4EBA fakeplayer.command.list.description=\u67E5\u770B\u6240\u6709\u5047\u4EBA
@ -48,7 +48,7 @@ fakeplayer.command.ride.stop.description=\u505C\u6B62\u9A91\u4E58
fakeplayer.command.fp.short-description=\u5047\u4EBA fakeplayer.command.fp.short-description=\u5047\u4EBA
fakeplayer.command.fp.full-description=\u53EF\u4EE5\u521B\u5EFA\u6A21\u62DF\u73A9\u5BB6\u7684\u5047\u4EBA\uFF0C\u4FDD\u6301\u533A\u5757\u7684\u5237\u65B0\uFF0C\u602A\u7269\u7684\u751F\u6210\u3002\u540C\u65F6\u63D0\u4F9B\u4E00\u4E9B\u547D\u4EE4\u8BA9\u4F60\u64CD\u63A7\u5047\u4EBA\u7684\u52A8\u4F5C\u3002 fakeplayer.command.fp.full-description=\u53EF\u4EE5\u521B\u5EFA\u6A21\u62DF\u73A9\u5BB6\u7684\u5047\u4EBA\uFF0C\u4FDD\u6301\u533A\u5757\u7684\u5237\u65B0\uFF0C\u602A\u7269\u7684\u751F\u6210\u3002\u540C\u65F6\u63D0\u4F9B\u4E00\u4E9B\u547D\u4EE4\u8BA9\u4F60\u64CD\u63A7\u5047\u4EBA\u7684\u52A8\u4F5C\u3002
fakeplayer.config.collidable=\u78B0\u649E\u7BB1 fakeplayer.config.collidable=\u78B0\u649E\u7BB1
fakeplayer.config.invulnerable=\u65E0\u654C fakeplayer.config.invulnerable=\u65E0\u654C\u6A21\u5F0F
fakeplayer.config.look_at_entity=\u76EE\u89C6\u5B9E\u4F53 fakeplayer.config.look_at_entity=\u76EE\u89C6\u5B9E\u4F53
fakeplayer.config.pickup_items=\u62FE\u53D6\u7269\u54C1 fakeplayer.config.pickup_items=\u62FE\u53D6\u7269\u54C1
fakeplayer.config.skin=\u4F7F\u7528\u76AE\u80A4 fakeplayer.config.skin=\u4F7F\u7528\u76AE\u80A4
@ -120,3 +120,13 @@ fakeplayer.command.config.set.error.invalid-value=\u672A\u77E5\u914D\u7F6E\u503C
fakeplayer.command.generic.error.non-fake-player=\u4F60\u8FD8\u6CA1\u53EC\u5524\u5047\u4EBA\u5462 fakeplayer.command.generic.error.non-fake-player=\u4F60\u8FD8\u6CA1\u53EC\u5524\u5047\u4EBA\u5462
fakeplayer.command.skin.description=\u62F7\u8D1D\u53E6\u5916\u4E00\u540D\u73A9\u5BB6\u7684\u76AE\u80A4 fakeplayer.command.skin.description=\u62F7\u8D1D\u53E6\u5916\u4E00\u540D\u73A9\u5BB6\u7684\u76AE\u80A4
fakeplayer.command.skin.error.too-many-operations=\u64CD\u4F5C\u8FC7\u4E8E\u9891\u7E41, \u4F60\u77ED\u65F6\u95F4\u5185\u4E0D\u80FD\u62F7\u8D1D\u79BB\u7EBF\u73A9\u5BB6\u7684\u76AE\u80A4, \u8BD5\u8BD5\u5728\u7EBF\u73A9\u5BB6\u7684\u5427\uFF5E fakeplayer.command.skin.error.too-many-operations=\u64CD\u4F5C\u8FC7\u4E8E\u9891\u7E41, \u4F60\u77ED\u65F6\u95F4\u5185\u4E0D\u80FD\u62F7\u8D1D\u79BB\u7EBF\u73A9\u5BB6\u7684\u76AE\u80A4, \u8BD5\u8BD5\u5728\u7EBF\u73A9\u5BB6\u7684\u5427\uFF5E
fakeplayer.command.refill.description=\u8BBE\u7F6E\u81EA\u52A8\u586B\u88C5
fakeplayer.manager.inventory.title=<name> \u7684\u7269\u54C1\u680F
fakeplayer.command.invsee.error.not-the-same-world=\u53EA\u80FD\u6253\u5F00\u540C\u4E00\u4E2A\u4E16\u754C\u7684\u5047\u4EBA\u80CC\u5305
fakeplayer.command.refill.success=<name> \u81EA\u52A8\u88C5\u586B\u5DF2 <status>
fakeplayer.generic.enabled=\u542F\u7528
fakeplayer.generic.disabled=\u7981\u7528
fakeplayer.config.refillable=\u81EA\u52A8\u586B\u88C5
fakeplayer.command.invsee.description=\u67E5\u770B\u80CC\u5305
fakeplayer.command.sleep.description=\u7761\u89C9
fakeplayer.command.wakeup.description=\u8D77\u5E8A

View File

@ -7,6 +7,9 @@ website: 'https://github.com/tanyaofei/minecraft-fakeplayer'
depend: depend:
- CommandAPI - CommandAPI
softdepend:
- OpenInv
permissions: permissions:
fakeplayer.all: fakeplayer.all:
description: '假人所有基础权限(即将删除, 请替换成 `fakeplayer.basic`)' description: '假人所有基础权限(即将删除, 请替换成 `fakeplayer.basic`)'

11
pom.xml
View File

@ -41,6 +41,10 @@
<id>dmulloy2-repo</id> <id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url> <url>https://repo.dmulloy2.net/repository/public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencyManagement> <dependencyManagement>
@ -103,6 +107,13 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.jikoo.OpenInv</groupId>
<artifactId>openinvapi</artifactId>
<version>4.4.0</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>