优化代码

This commit is contained in:
tanyaofei 2023-07-17 18:38:47 +08:00
parent 35ef7644b9
commit 0651bb0171
12 changed files with 208 additions and 190 deletions

View File

@ -3,7 +3,7 @@ package io.github.hello09x.fakeplayer;
import io.github.hello09x.fakeplayer.command.FakePlayerCommand;
import io.github.hello09x.fakeplayer.listener.PlayerDeathListener;
import io.github.hello09x.fakeplayer.listener.PlayerQuitListener;
import io.github.hello09x.fakeplayer.listener.PlayerTeleportListener;
import io.github.hello09x.fakeplayer.manager.FakePlayerManager;
import lombok.Getter;
import org.bukkit.plugin.java.JavaPlugin;
@ -22,15 +22,14 @@ public final class Main extends JavaPlugin {
}
{
// getServer().getPluginManager().registerEvents(PlayerQuitListener.instance, Main.getInstance());
getServer().getPluginManager().registerEvents(PlayerQuitListener.instance, Main.getInstance());
getServer().getPluginManager().registerEvents(PlayerDeathListener.instance, Main.getInstance());
// getServer().getPluginManager().registerEvents(PlayerTeleportListener.instance, Main.getInstance());
}
}
@Override
public void onDisable() {
// Plugin shutdown logic
FakePlayerManager.instance.removeFakePlayers();
}
}

View File

@ -7,6 +7,7 @@ import net.kyori.adventure.sound.Sound;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -90,18 +91,7 @@ public abstract class AbstractTeleportCommand extends ExecutableCommand {
}
protected void teleport(@NotNull Player from, @NotNull Player to) {
from.teleport(to.getLocation());
from.playSound(Sound.sound(
ENTITY_ENDERMAN_TELEPORT.key(),
Sound.Source.PLAYER,
1.0F,
1.0F
));
to.playSound(Sound.sound(
ENTITY_ENDERMAN_TELEPORT.key(),
Sound.Source.PLAYER,
1.0F,
1.0F
));
from.teleport(to.getLocation(), PlayerTeleportEvent.TeleportCause.COMMAND);
to.getLocation().getWorld().playSound(to.getLocation(), ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F);
}
}

View File

@ -91,8 +91,7 @@ public class RemoveCommand extends ExecutableCommand {
return fakes
.stream()
.map(Player::getName)
.filter(name -> args[0].isBlank()
|| name.toLowerCase().contains(args[0].toLowerCase()))
.filter(name -> args[0].isBlank() || name.toLowerCase().contains(args[0].toLowerCase()))
.toList();
}
}

View File

@ -46,11 +46,6 @@ public class TpHereCommand extends AbstractTeleportCommand {
return true;
}
if (!fake.getWorld().equals(creator.getWorld())) {
sender.sendMessage(text("暂不支持跨世界传送...", RED));
return true;
}
teleport(fake, creator);
return true;
}

View File

@ -0,0 +1,67 @@
package io.github.hello09x.fakeplayer.core;
import com.mojang.datafixers.DataFixer;
import io.github.hello09x.fakeplayer.util.ReflectionUtils;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.Map;
public class EmptyAdvancements extends PlayerAdvancements {
public final static Field PROGRESS = ReflectionUtils.getFirstFieldByType(PlayerAdvancements.class, Map.class, false);
public EmptyAdvancements(
DataFixer datafixer,
PlayerList playerlist,
ServerAdvancementManager manager,
Path path,
ServerPlayer player
) {
super(datafixer, playerlist, manager, path, player);
this.save();
}
@Override
public boolean award(Advancement advancement, String s) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
}
@Override
public AdvancementProgress getOrStartProgress(Advancement advancement) {
return new AdvancementProgress();
}
@Override
public boolean revoke(Advancement advancement, String s) {
return false;
}
@Override
public void save() {
}
@Override
public void setPlayer(ServerPlayer player) {
}
@Override
public void setSelectedTab(Advancement advancement) {
}
@Override
public void stopListening() {
}
}

View File

@ -2,12 +2,16 @@ package io.github.hello09x.fakeplayer.entity;
import com.mojang.authlib.GameProfile;
import io.github.hello09x.fakeplayer.Main;
import io.github.hello09x.fakeplayer.core.EmptyAdvancements;
import io.github.hello09x.fakeplayer.core.EmptyConnection;
import io.github.hello09x.fakeplayer.core.EmptyNetworkManager;
import io.github.hello09x.fakeplayer.util.ReflectionUtils;
import lombok.Getter;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@ -15,6 +19,7 @@ import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.ChatVisiblity;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
@ -22,31 +27,33 @@ import org.bukkit.entity.Player;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import static org.bukkit.Sound.ENTITY_ENDERMAN_TELEPORT;
public class FakePlayer extends ServerPlayer {
private final UUID uniqueId;
public final static Field advancements = ReflectionUtils.getFirstFieldByType(ServerPlayer.class, PlayerAdvancements.class, false);
private final String name;
private final Location spawnLocation;
@Getter
private @NotNull
final Location spawnLocation;
public FakePlayer(
MinecraftServer server,
ServerLevel world,
UUID uniqueId,
String name,
Location at
@NotNull MinecraftServer server,
@NotNull ServerLevel world,
@NotNull UUID uniqueId,
@NotNull String name,
@NotNull Location at
) {
super(server, world, new GameProfile(uniqueId, name));
this.uniqueId = uniqueId;
this.name = name;
this.spawnLocation = at;
try {
@ -56,9 +63,25 @@ public class FakePlayer extends ServerPlayer {
} catch (IOException e) {
throw new RuntimeException(e);
}
if (advancements != null) {
try {
advancements.set(
this,
new EmptyAdvancements(
server.getFixerUpper(),
server.getPlayerList(),
server.getAdvancements(),
Main.getInstance().getDataFolder().getParentFile().toPath(),
this
));
} catch (IllegalAccessException ignored) {
}
}
}
private static boolean isChunkLoaded(Location at) {
private static boolean isChunkLoaded(@NotNull Location at) {
if (at.getWorld() == null) {
return false;
}
@ -67,17 +90,21 @@ public class FakePlayer extends ServerPlayer {
return at.getWorld().isChunkLoaded(x, z);
}
public Player prepare() {
addToPlayerList();
spawn();
public @NotNull Player spawn() {
this.boardcast();
this.addEntityToWorld();
var p = Objects.requireNonNull(Bukkit.getPlayer(this.uuid));
p.setSleepingIgnored(true);
p.setPersistent(false);
p.setInvulnerable(true);
new BukkitRunnable() {
@Override
public void run() {
if (!p.isOnline()) {
cancel();
}
doTick();
tickCount++;
}
@ -86,9 +113,9 @@ public class FakePlayer extends ServerPlayer {
}
@SuppressWarnings("all")
public void addToPlayerList() {
var packet = new ServerboundClientInformationPacket(
"EN",
public void boardcast() {
this.updateOptions(new ServerboundClientInformationPacket(
"en_us",
10,
ChatVisiblity.FULL,
false,
@ -96,14 +123,14 @@ public class FakePlayer extends ServerPlayer {
HumanoidArm.LEFT,
false,
true
);
this.updateOptions(packet);
));
var entity = this.getBukkitEntity();
var handle = (ServerPlayer) ((CraftEntity) entity).getHandle();
if (handle.level() == null) {
return;
}
var playerList = handle.level().players();
if (!playerList.contains(handle)) {
((List) playerList).add(handle);
@ -132,15 +159,15 @@ public class FakePlayer extends ServerPlayer {
}
public void spawn() {
public void addEntityToWorld() {
var entity = this.getBukkitEntity();
var handle = (ServerPlayer) ((CraftEntity) entity).getHandle();
if (!isChunkLoaded(spawnLocation)) {
spawnLocation.getChunk().load();
}
handle.level().addFreshEntity(handle, CreatureSpawnEvent.SpawnReason.CUSTOM);
handle.level().addFreshEntity(handle, CreatureSpawnEvent.SpawnReason.CUSTOM);
((CraftServer) Bukkit.getServer()).getHandle().respawn(
this,
true,
@ -149,6 +176,7 @@ public class FakePlayer extends ServerPlayer {
// move directly
getBukkitEntity().teleport(spawnLocation);
spawnLocation.getWorld().playSound(spawnLocation, Sound.ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F);
}

View File

@ -19,6 +19,7 @@ public class PlayerDeathListener implements Listener {
return;
}
// 假人不会复活, 死掉了就踢掉
player.kick();
}

View File

@ -1,35 +0,0 @@
package io.github.hello09x.fakeplayer.listener;
import io.github.hello09x.fakeplayer.manager.FakePlayerManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.NotNull;
public class PlayerTeleportListener implements Listener {
public final static PlayerTeleportListener instance = new PlayerTeleportListener();
private final FakePlayerManager manager = FakePlayerManager.instance;
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void handlePlayerTeleport(@NotNull PlayerTeleportEvent event) {
if (!manager.isFakePlayer(event.getPlayer())) {
return;
}
// 创建玩家时会使用这个传送原因
if (event.getCause() == PlayerTeleportEvent.TeleportCause.SPECTATE) {
return;
}
var from = event.getFrom().getWorld().getUID();
var to = event.getTo().getWorld().getUID();
if (from.equals(to)) {
return;
}
event.setCancelled(true);
}
}

View File

@ -1,6 +1,5 @@
package io.github.hello09x.fakeplayer.manager;
import com.mojang.authlib.GameProfile;
import io.github.hello09x.fakeplayer.Main;
import io.github.hello09x.fakeplayer.entity.FakePlayer;
import org.bukkit.Bukkit;
@ -25,8 +24,16 @@ public class FakePlayerManager {
public final static FakePlayerManager instance = new FakePlayerManager();
private final static String META_KEY_CREATOR = "fakeplayer:creator";
private final FakeplayerProperties properties = FakeplayerProperties.instance;
/**
* 创建一个假人
*
* @param creator 创建者
* @param at 生成地点
*/
public synchronized void spawnFakePlayer(
@NotNull CommandSender creator,
@NotNull Location at
@ -50,9 +57,8 @@ public class FakePlayerManager {
UUID.randomUUID(),
name,
at
);
var p = player.prepare();
p.setMetadata(FakePlayerSpawner.META_KEY_CREATOR, new FixedMetadataValue(Main.getInstance(), creator.getName()));
).spawn();
player.setMetadata(META_KEY_CREATOR, new FixedMetadataValue(Main.getInstance(), creator.getName()));
}
public @Nullable Player getFakePlayer(@NotNull CommandSender creator, @NotNull String name) {
@ -69,6 +75,12 @@ public class FakePlayerManager {
return fake;
}
/**
* 根据名称获取假人
*
* @param name 名称
* @return 假人
*/
public @Nullable Player getFakePlayer(@NotNull String name) {
var player = Bukkit.getServer().getPlayer(name);
if (player == null) {
@ -82,6 +94,12 @@ public class FakePlayerManager {
return player;
}
/**
* 移除指定创建者创建的假人
*
* @param creator 创建者
* @return 移除假人的数量
*/
public int removeFakePlayers(@NotNull Player creator) {
var fakes = getFakePlayers(creator);
for (var f : fakes) {
@ -90,8 +108,14 @@ public class FakePlayerManager {
return fakes.size();
}
/**
* 获取一个假人的创建者, 如果这个玩家不是假人, 则为 {@code null}
*
* @param fakePlayer 假人
* @return 假人的创建者
*/
public @Nullable String getCreator(@NotNull Player fakePlayer) {
var meta = fakePlayer.getMetadata(FakePlayerSpawner.META_KEY_CREATOR);
var meta = fakePlayer.getMetadata(META_KEY_CREATOR);
if (meta.isEmpty()) {
return null;
}
@ -99,6 +123,11 @@ public class FakePlayerManager {
return meta.get(0).asString();
}
/**
* 移除所有假人
*
* @return 移除的假人数量
*/
public int removeFakePlayers() {
var fakes = getFakePlayers();
for (var f : fakes) {
@ -107,29 +136,44 @@ public class FakePlayerManager {
return fakes.size();
}
/**
* @return 获取所有假人
*/
public @NotNull List<Player> getFakePlayers() {
return Bukkit
.getServer()
.getOnlinePlayers()
.stream()
.filter(p -> !p.getMetadata(FakePlayerSpawner.META_KEY_CREATOR).isEmpty())
.filter(p -> !p.getMetadata(META_KEY_CREATOR).isEmpty())
.collect(Collectors.toList());
}
/**
* 获取创建者创建的所有假人
*
* @param creator 创建者
* @return 创建者创建的假人
*/
public @NotNull List<Player> getFakePlayers(@NotNull CommandSender creator) {
var name = creator.getName();
return Bukkit
.getServer()
.getOnlinePlayers()
.stream()
.filter(p -> p.getMetadata(FakePlayerSpawner.META_KEY_CREATOR)
.filter(p -> p.getMetadata(META_KEY_CREATOR)
.stream()
.anyMatch(meta -> meta.asString().equals(name)))
.collect(Collectors.toList());
}
/**
* 判断一名玩家是否是假人
*
* @param player 玩家
* @return 是否是假人
*/
public boolean isFakePlayer(@NotNull Player player) {
return !player.getMetadata(FakePlayerSpawner.META_KEY_CREATOR).isEmpty();
return !player.getMetadata(META_KEY_CREATOR).isEmpty();
}

View File

@ -1,99 +0,0 @@
package io.github.hello09x.fakeplayer.manager;
import com.mojang.authlib.GameProfile;
import io.github.hello09x.fakeplayer.Main;
import io.github.hello09x.fakeplayer.core.EmptyNetworkManager;
import net.kyori.adventure.sound.Sound;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import static org.bukkit.Sound.ENTITY_ENDERMAN_TELEPORT;
public class FakePlayerSpawner {
public final static String META_KEY_CREATOR = "fakeplayer::creator";
// public static @NotNull Player spawn(
// @NotNull UUID uniqueId,
// @NotNull String name,
// @NotNull Player creator,
// @NotNull Location at
// ) {
// var craftServer = (CraftServer) Bukkit.getServer();
// var server = craftServer.getServer();
// var gameProfile = new GameProfile(uniqueId, name);
// var world = ((CraftWorld) at.getWorld()).getHandle();
//
// var entityPlayer = new EntityPlayer(server, world, gameProfile);
// var craftPlayer = entityPlayer.getBukkitEntity();
//
//
// entityPlayer.f = 0; // ping
// entityPlayer.c = new PlayerConnection(
// server,
// new EmptyNetworkManager(EnumProtocolDirection.a),
// entityPlayer
// );
//
// craftPlayer.setFirstPlayed(System.currentTimeMillis());
// craftServer.getHandle().respawn(
// entityPlayer,
// true,
// PlayerRespawnEvent.RespawnReason.PLUGIN
// );
// refreshTabList(entityPlayer);
//
// var holder = Objects.requireNonNull(Bukkit.getPlayer(uniqueId));
// holder.setViewDistance(9);
// holder.setAffectsSpawning(true);
// holder.setSleepingIgnored(true);
// holder.setPersistent(false);
// holder.setGameMode(GameMode.CREATIVE);
// holder.setAllowFlight(true);
// holder.setMetadata(META_KEY_CREATOR, new FixedMetadataValue(Main.getInstance(), creator.getUniqueId().toString()));
// holder.teleport(at, PlayerTeleportEvent.TeleportCause.SPECTATE);
// creator.playSound(Sound.sound(
// ENTITY_ENDERMAN_TELEPORT.key(),
// Sound.Source.PLAYER,
// 1.0F,
// 1.0F
// ));
//
// CompletableFuture.runAsync(() -> {
// System.out.println(holder);
// System.out.println(craftPlayer);
// });
// return holder;
// }
//
// private static void refreshTabList(EntityPlayer player) {
// try {
// var field = EntityPlayer.class.getDeclaredField("cU");
// field.setAccessible(true);
// field.set(player, true);
// } catch (NoSuchFieldException | IllegalAccessException e) {
// throw new RuntimeException(e);
// }
//
// for (var online : Bukkit.getOnlinePlayers()) {
// ((CraftPlayer) online).getHandle().c.a(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.a.a, player)); //ADD_PLAYER
// ((CraftPlayer) online).getHandle().c.a(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.a.d, player)); //UPDATE_LISTED
// }
// }
}

View File

@ -0,0 +1,26 @@
package io.github.hello09x.fakeplayer.util;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectionUtils {
private final static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
public static @Nullable Field getFirstFieldByType(Class<?> clazz, Class<?> fieldType, boolean includeStatic) {
for (var field : clazz.getDeclaredFields()) {
if (includeStatic ^ Modifier.isStatic(field.getModifiers())) {
continue;
}
if (field.getType() == fieldType) {
field.setAccessible(true);
return field;
}
}
return null;
}
}

View File

@ -12,6 +12,9 @@ commands:
permissions:
fakeplayer:
default: true
fakeplayer.admin:
description: '假人基础权限'
default: op
fakeplayer.admin:
description: '假人管理员权限'
default: op