Notify creator when his fake player is dead

This commit is contained in:
tanyaofei 2024-08-07 14:49:27 +08:00
parent 492348420d
commit a778ae4b78
13 changed files with 75 additions and 71 deletions

View File

@ -2,6 +2,7 @@ package io.github.hello09x.fakeplayer.core;
import com.google.inject.Guice;
import com.google.inject.Injector;
import io.github.hello09x.devtools.command.CommandModule;
import io.github.hello09x.devtools.core.TranslationModule;
import io.github.hello09x.devtools.core.translation.TranslationConfig;
import io.github.hello09x.devtools.core.translation.TranslatorUtils;
@ -39,6 +40,7 @@ public final class Main extends JavaPlugin {
injector = Guice.createInjector(
new FakeplayerModule(),
new CommandModule(),
new DatabaseModule(),
new TranslationModule(new TranslationConfig(
"message/message",

View File

@ -7,6 +7,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.text.Component.translatable;
@Singleton
public class RespawnCommand extends AbstractCommand {
@ -16,6 +18,7 @@ public class RespawnCommand extends AbstractCommand {
public void respawn(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = super.getTarget(sender, args, Entity::isDead);
bridge.fromPlayer(target).respawn();
sender.sendMessage(translatable("fakeplayer.command.generic.success"));
}
}

View File

@ -3,8 +3,8 @@ package io.github.hello09x.fakeplayer.core.command.impl;
import com.google.common.base.Throwables;
import com.google.inject.Singleton;
import dev.jorel.commandapi.executors.CommandArguments;
import io.github.hello09x.devtools.core.message.IMessageException;
import io.github.hello09x.devtools.core.message.MessageException;
import io.github.hello09x.devtools.command.exception.CommandException;
import io.github.hello09x.devtools.command.exception.HandleCommandException;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.util.Mth;
import net.kyori.adventure.text.Component;
@ -14,6 +14,7 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
@ -29,6 +30,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.*;
public class SpawnCommand extends AbstractCommand {
private final static DateTimeFormatter REMOVE_AT_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
private final static BukkitScheduler scheduler = Bukkit.getScheduler();
private static String toLocationString(@NotNull Location location) {
return location.getWorld().getName()
@ -42,6 +44,7 @@ public class SpawnCommand extends AbstractCommand {
/**
* 创建假人
*/
@HandleCommandException
public void spawn(@NotNull CommandSender sender, @NotNull CommandArguments args) {
var name = (String) args.get("name");
if (name != null && name.isEmpty()) {
@ -65,47 +68,42 @@ public class SpawnCommand extends AbstractCommand {
}
var removedAt = Optional.ofNullable(config.getLifespan()).map(lifespan -> LocalDateTime.now().plus(lifespan)).orElse(null);
try {
manager.spawnAsync(sender, name, spawnpoint, Optional.ofNullable(config.getLifespan()).map(Duration::toMillis).orElse(-1L))
.thenAcceptAsync(player -> {
if (player == null) {
return;
manager.spawnAsync(sender, name, spawnpoint, Optional.ofNullable(config.getLifespan()).map(Duration::toMillis).orElse(-1L))
.thenAcceptAsync(player -> {
if (player == null) {
return;
}
Component message;
if (removedAt == null) {
message = translatable(
"fakeplayer.command.spawn.success.without-lifespan",
text(player.getName(), WHITE),
text(toLocationString(spawnpoint), WHITE)
).color(GRAY);
} else {
message = translatable(
"fakeplayer.command.spawn.success.with-lifespan",
text(player.getName(), WHITE),
text(toLocationString(spawnpoint), WHITE),
text(REMOVE_AT_FORMATTER.format(removedAt))
).color(GRAY);
}
scheduler.runTask(Main.getInstance(), () -> {
sender.sendMessage(message);
if (sender instanceof Player p && manager.countByCreator(sender) == 1) {
// 有些命令在有假人的时候才会显示, 因此需要强制刷新一下
p.updateCommands();
}
Component message;
if (removedAt == null) {
message = translatable(
"fakeplayer.command.spawn.success.without-lifespan",
text(player.getName(), WHITE),
text(toLocationString(spawnpoint), WHITE)
).color(GRAY);
} else {
message = translatable(
"fakeplayer.command.spawn.success.with-lifespan",
text(player.getName(), WHITE),
text(toLocationString(spawnpoint), WHITE),
text(REMOVE_AT_FORMATTER.format(removedAt))
).color(GRAY);
}
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
sender.sendMessage(message);
if (sender instanceof Player p && manager.countByCreator(sender) == 1) {
// 有些命令在有假人的时候才会显示, 因此需要强制刷新一下
p.updateCommands();
}
});
}).exceptionally(e -> {
if (Throwables.getRootCause(e) instanceof IMessageException me) {
Bukkit.getScheduler().runTask(Main.getInstance(), () -> sender.sendMessage(me.getComponent()));
} else {
Bukkit.getScheduler().runTask(Main.getInstance(), () -> sender.sendMessage(translatable("fakeplayer.command.spawn.error.unknown", RED)));
log.severe(Throwables.getStackTraceAsString(e));
}
return null;
});
} catch (MessageException e) {
sender.sendMessage(e.getComponent());
}
}).exceptionally(e -> {
if (Throwables.getRootCause(e) instanceof CommandException ce) {
scheduler.runTask(Main.getInstance(), () -> sender.sendMessage(ce.component()));
} else {
scheduler.runTask(Main.getInstance(), () -> sender.sendMessage(translatable("fakeplayer.command.spawn.error.unknown", RED)));
log.severe(Throwables.getStackTraceAsString(e));
}
return null;
});
}

View File

@ -1,6 +1,6 @@
package io.github.hello09x.fakeplayer.core.entity;
import io.github.hello09x.devtools.core.message.RuntimeMessageException;
import io.github.hello09x.devtools.command.exception.CommandException;
import io.github.hello09x.devtools.core.utils.EntityUtils;
import io.github.hello09x.devtools.core.utils.SchedulerUtils;
import io.github.hello09x.devtools.core.utils.WorldUtils;
@ -121,7 +121,7 @@ public class FakePlayer {
.runTaskAsynchronously(Main.getInstance(), () -> {
var event = this.callPreLoginEvent(address);
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
throw new RuntimeMessageException(translatable(
throw new CommandException(translatable(
"fakeplayer.command.spawn.error.disallowed",
text(player.getName(), WHITE),
event.kickMessage()
@ -132,7 +132,7 @@ public class FakePlayer {
{
var event = this.callLoginEvent(address);
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED && config.getPreventKicking().ordinal() < PreventKicking.ON_SPAWNING.ordinal()) {
throw new RuntimeMessageException(translatable(
throw new CommandException(translatable(
"fakeplayer.command.spawn.error.disallowed", RED,
text(player.getName(), WHITE),
event.kickMessage()

View File

@ -32,8 +32,9 @@ import java.util.Optional;
import java.util.logging.Logger;
import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
import static net.kyori.adventure.text.event.ClickEvent.runCommand;
import static net.kyori.adventure.text.format.NamedTextColor.*;
import static net.kyori.adventure.text.format.TextDecoration.UNDERLINED;
@Singleton
public class FakeplayerListener implements Listener {
@ -118,12 +119,20 @@ public class FakeplayerListener implements Listener {
* 死亡退出游戏
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void kickOnDead(@NotNull PlayerDeathEvent event) {
public void kickOrNotifyOnDead(@NotNull PlayerDeathEvent event) {
var player = event.getPlayer();
if (manager.isNotFake(player)) {
return;
}
if (!config.isKickOnDead()) {
var creator = manager.getCreator(player);
if (creator != null) {
creator.sendMessage(translatable(
"fakeplayer.listener.death.notify",
text(player.getName(), GOLD),
text("/fp respawn", DARK_GREEN, UNDERLINED).clickEvent(runCommand("/fp respawn " + player.getName()))
).color(RED));
}
return;
}

View File

@ -2,8 +2,7 @@ package io.github.hello09x.fakeplayer.core.manager;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.github.hello09x.devtools.core.message.MessageException;
import io.github.hello09x.devtools.core.message.RuntimeMessageException;
import io.github.hello09x.devtools.command.exception.CommandException;
import io.github.hello09x.devtools.core.utils.Exceptions;
import io.github.hello09x.devtools.core.utils.MetadataUtils;
import io.github.hello09x.fakeplayer.api.spi.Action;
@ -15,8 +14,6 @@ import io.github.hello09x.fakeplayer.core.entity.FakePlayer;
import io.github.hello09x.fakeplayer.core.entity.SpawnOption;
import io.github.hello09x.fakeplayer.core.manager.invsee.Invsee;
import io.github.hello09x.fakeplayer.core.manager.naming.NameManager;
import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName;
import io.github.hello09x.fakeplayer.core.manager.naming.exception.IllegalCustomNameException;
import io.github.hello09x.fakeplayer.core.repository.model.Config;
import io.github.hello09x.fakeplayer.core.util.AddressUtils;
import io.github.hello09x.fakeplayer.core.util.Commands;
@ -93,15 +90,10 @@ public class FakeplayerManager {
@Nullable String name,
@NotNull Location spawnAt,
long lifespan
) throws MessageException {
) {
this.checkLimit(creator);
SequenceName sn;
try {
sn = name == null ? nameManager.register(creator) : nameManager.specify(name);
} catch (IllegalCustomNameException e) {
throw new RuntimeMessageException(e.getMessage());
}
var sn = name == null ? nameManager.register(creator) : nameManager.specify(name);
var fp = new FakePlayer(
creator,
@ -527,23 +519,22 @@ public class FakeplayerManager {
* 检测限制, 不满足条件则抛出异常
*
* @param creator 创建者
* @throws MessageException 消息
*/
private void checkLimit(@NotNull CommandSender creator) throws MessageException {
private void checkLimit(@NotNull CommandSender creator) throws CommandException {
if (creator.isOp()) {
return;
}
if (this.playerList.count() >= this.config.getServerLimit()) {
throw new MessageException(translatable("fakeplayer.command.spawn.error.server-limit"));
throw new CommandException(translatable("fakeplayer.command.spawn.error.server-limit"));
}
if (this.playerList.getByCreator(creator.getName()).size() >= this.config.getPlayerLimit()) {
throw new MessageException(translatable("fakeplayer.command.spawn.error.player-limit"));
throw new CommandException(translatable("fakeplayer.command.spawn.error.player-limit"));
}
if (this.config.isDetectIp() && this.countByAddress(AddressUtils.getAddress(creator)) >= this.config.getPlayerLimit()) {
throw new MessageException(translatable("fakeplayer.command.spawn.error.ip-limit"));
throw new CommandException(translatable("fakeplayer.command.spawn.error.ip-limit"));
}
}

View File

@ -1,18 +1,13 @@
package io.github.hello09x.fakeplayer.core.manager.naming.exception;
import lombok.Getter;
import io.github.hello09x.devtools.command.exception.CommandException;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.jetbrains.annotations.NotNull;
public class IllegalCustomNameException extends IllegalArgumentException {
@Getter
private final Component text;
public class IllegalCustomNameException extends CommandException {
public IllegalCustomNameException(@NotNull Component message) {
super(PlainTextComponentSerializer.plainText().serialize(message));
this.text = message;
super(message);
}
}

View File

@ -128,6 +128,7 @@ fakeplayer.direction.south=south
fakeplayer.direction.up=up
fakeplayer.direction.west=west
fakeplayer.listener.login.deny-used-uuid=Your UUID was used by fake player, disallow to login
fakeplayer.listener.death.notify=Fake player {0} is dead, you could use {1} to respawn him
fakeplayer.manager.inventory.title=Inventory of {0}
fakeplayer.manager.remove-all-on-low-tps=[Low tps, all fake players have been removed]
fakeplayer.spawn.error.name.existed=This name already existed

View File

@ -127,6 +127,7 @@ fakeplayer.direction.north=north
fakeplayer.direction.south=south
fakeplayer.direction.up=up
fakeplayer.direction.west=west
fakeplayer.listener.death.notify=Fake player {0} is dead, you could use {1} to respawn him
fakeplayer.listener.login.deny-used-uuid=Your UUID was used by fake player, disallow to login
fakeplayer.manager.inventory.title=Inventory of {0}
fakeplayer.manager.remove-all-on-low-tps=[Low tps, all fake players have been removed]

View File

@ -127,6 +127,7 @@ fakeplayer.direction.north=\u5317\u65B9
fakeplayer.direction.south=\u5357\u65B9
fakeplayer.direction.up=\u4E0A\u65B9
fakeplayer.direction.west=\u897F\u65B9
fakeplayer.listener.death.notify=\u5047\u4EBA {0} \u6B7B\u4EA1, \u53EF\u4EE5\u4F7F\u7528 {1} \u53BB\u590D\u6D3B\u4ED6
fakeplayer.listener.login.deny-used-uuid=\u4F60\u7684 UUID \u88AB\u4F7F\u7528\u8FC7, \u4E0D\u80FD\u767B\u5F55\u670D\u52A1\u5668
fakeplayer.manager.inventory.title={0} \u7684\u7269\u54C1\u680F
fakeplayer.manager.remove-all-on-low-tps=[\u670D\u52A1\u5668\u8FC7\u4E8E\u5361\u987F, \u5DF2\u79FB\u9664\u6240\u6709\u5047\u4EBA]

View File

@ -127,6 +127,7 @@ fakeplayer.direction.north=\u5317\u65B9
fakeplayer.direction.south=\u5357\u65B9
fakeplayer.direction.up=\u4E0A\u65B9
fakeplayer.direction.west=\u897F\u65B9
fakeplayer.listener.death.notify=\u5047\u4EBA {0} \u6B7B\u4EA1, \u53EF\u4EE5\u4F7F\u7528 {1} \u6765\u590D\u6D3B\u4ED6
fakeplayer.listener.login.deny-used-uuid=\u4F60\u7684 UUID \u88AB\u4F7F\u7528\u8FC7, \u4E0D\u80FD\u767B\u5F55\u670D\u52A1\u5668
fakeplayer.manager.inventory.title={0} \u7684\u7269\u54C1\u680F
fakeplayer.manager.remove-all-on-low-tps=[\u670D\u52A1\u5668\u8FC7\u4E8E\u5361\u987F, \u5DF2\u79FB\u9664\u6240\u6709\u5047\u4EBA]

View File

@ -127,6 +127,7 @@ fakeplayer.direction.north=\u5317\u65B9
fakeplayer.direction.south=\u5357\u65B9
fakeplayer.direction.up=\u4E0A\u65B9
fakeplayer.direction.west=\u897F\u65B9
fakeplayer.listener.death.notify=\u5047\u4EBA {0} \u6B7B\u4EA1, \u53EF\u4EE5\u4F7F\u7528 {1} \u569F\u5FA9\u6D3B\u4F62
fakeplayer.listener.login.deny-used-uuid=\u4F60\u5605 UUID \u88AB\u4F7F\u7528\u904E, \u5514\u53EF\u4EE5\u767B\u9304\u4F3A\u670D\u5668
fakeplayer.manager.inventory.title={0} \u7684\u7269\u54C1\u6B04
fakeplayer.manager.remove-all-on-low-tps=[\u7531\u4E8E\u4F3A\u670D\u5668 lag \u6A5F, \u5DF2\u79FB\u9664\u6240\u6709\u5047\u4EBA]

View File

@ -127,6 +127,7 @@ fakeplayer.direction.north=\u5317\u65B9
fakeplayer.direction.south=\u5357\u65B9
fakeplayer.direction.up=\u4E0A\u65B9
fakeplayer.direction.west=\u897F\u65B9
fakeplayer.listener.death.notify=\u5047\u4EBA {0} \u6B7B\u4EA1, \u53EF\u4EE5\u4F7F\u7528 \u00A7f/fp respawn\u00A7r \u4F86\u5FA9\u6D3B\u4ED6
fakeplayer.listener.login.deny-used-uuid=\u4F60\u7684 UUID \u88AB\u4F7F\u7528\u904E, \u4E0D\u80FD\u767B\u9304\u4F3A\u670D\u5668
fakeplayer.manager.inventory.title={0} \u7684\u7269\u54C1\u6B04
fakeplayer.manager.remove-all-on-low-tps=[\u4F3A\u670D\u5668\u904E\u65BC\u5361\u9813, \u5DF2\u79FB\u9664\u6240\u6709\u5047\u4EBA]