This commit is contained in:
tanyaofei 2023-07-17 17:03:19 +08:00
parent 3de5d680fc
commit 35ef7644b9
8 changed files with 334 additions and 97 deletions

View File

@ -22,9 +22,9 @@ 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());
// getServer().getPluginManager().registerEvents(PlayerTeleportListener.instance, Main.getInstance());
}
}

View File

@ -2,6 +2,7 @@ package io.github.hello09x.fakeplayer.command.player;
import io.github.hello09x.fakeplayer.manager.FakePlayerManager;
import io.github.tanyaofei.plugin.toolkit.command.ExecutableCommand;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -36,14 +37,29 @@ public class CreateCommand extends ExecutableCommand {
@NotNull String label,
@NotNull String[] args
) {
if (!(sender instanceof Player p)) {
sender.sendMessage(text("你不是玩家...", RED));
return true;
if (sender instanceof Player p) {
manager.spawnFakePlayer(
p,
((Player) sender).getLocation()
);
} else {
if (args.length != 3) {
return false;
}
double x, y, z;
try {
x = Double.parseDouble(args[0]);
y = Double.parseDouble(args[1]);
z = Double.parseDouble(args[2]);
} catch (NumberFormatException e) {
return false;
}
var world = sender.getServer().getWorlds().get(0);
manager.spawnFakePlayer(sender, new Location(world, x, y, z));
}
manager.spawnFakePlayer(
p,
((Player) sender).getLocation()
);
sender.sendMessage(text("创建成功", GRAY));
return true;
}

View File

@ -0,0 +1,18 @@
package io.github.hello09x.fakeplayer.core;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
public class EmptyConnection extends ServerGamePacketListenerImpl {
public EmptyConnection(MinecraftServer minecraftServer, Connection networkManager, ServerPlayer entityPlayer) {
super(minecraftServer, networkManager, entityPlayer);
}
@Override
public void send(Packet<?> packet) {
}
}

View File

@ -1,14 +1,30 @@
package io.github.hello09x.fakeplayer.core;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
public class EmptyNetworkManager extends NetworkManager {
import java.io.IOException;
public EmptyNetworkManager(EnumProtocolDirection enumprotocoldirection) {
super(enumprotocoldirection);
this.m = new EmptyChannel(null);
this.n = this.m.remoteAddress();
this.preparing = false;
public class EmptyNetworkManager extends Connection {
public EmptyNetworkManager(PacketFlow flag) throws IOException {
super(flag);
this.channel = new EmptyChannel(null);
this.address = this.channel.remoteAddress();
}
}
@Override
public boolean isConnected() {
return true;
}
@Override
public void send(Packet packet, PacketSendListener genericfuturelistener) {
}
@Override
public void handleDisconnection() {
super.handleDisconnection();
}
}

View File

@ -0,0 +1,21 @@
package io.github.hello09x.fakeplayer.core;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class EmptySocket extends Socket {
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(EMPTY);
}
@Override
public OutputStream getOutputStream() {
return new ByteArrayOutputStream(5);
}
private static final byte[] EMPTY = new byte[50];
}

View File

@ -0,0 +1,155 @@
package io.github.hello09x.fakeplayer.entity;
import com.mojang.authlib.GameProfile;
import io.github.hello09x.fakeplayer.Main;
import io.github.hello09x.fakeplayer.core.EmptyConnection;
import io.github.hello09x.fakeplayer.core.EmptyNetworkManager;
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.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.ChatVisiblity;
import org.bukkit.Bukkit;
import org.bukkit.Location;
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;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public class FakePlayer extends ServerPlayer {
private final UUID uniqueId;
private final String name;
private final Location spawnLocation;
public FakePlayer(
MinecraftServer server,
ServerLevel world,
UUID uniqueId,
String name,
Location at
) {
super(server, world, new GameProfile(uniqueId, name));
this.uniqueId = uniqueId;
this.name = name;
this.spawnLocation = at;
try {
var networkManager = new EmptyNetworkManager(PacketFlow.CLIENTBOUND);
this.connection = new EmptyConnection(server, networkManager, this);
networkManager.setListener(this.connection);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static boolean isChunkLoaded(Location at) {
if (at.getWorld() == null) {
return false;
}
int x = at.getBlockX() >> 4;
int z = at.getBlockZ() >> 4;
return at.getWorld().isChunkLoaded(x, z);
}
public Player prepare() {
addToPlayerList();
spawn();
var p = Objects.requireNonNull(Bukkit.getPlayer(this.uuid));
p.setSleepingIgnored(true);
p.setPersistent(false);
new BukkitRunnable() {
@Override
public void run() {
doTick();
tickCount++;
}
}.runTaskTimer(Main.getInstance(), 0, 1);
return p;
}
@SuppressWarnings("all")
public void addToPlayerList() {
var packet = new ServerboundClientInformationPacket(
"EN",
10,
ChatVisiblity.FULL,
false,
0,
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);
}
try {
var updatePlayerStatus = ChunkMap.class.getDeclaredMethod(
"a",
ServerPlayer.class,
boolean.class
);
updatePlayerStatus.setAccessible(true);
updatePlayerStatus.invoke(
((ServerLevel) handle.level()).getChunkSource().chunkMap,
handle,
true
);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
for(var online: Bukkit.getOnlinePlayers()) {
((CraftPlayer) online).getHandle().connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, handle));
((CraftPlayer) online).getHandle().connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, handle));
}
}
public void spawn() {
var entity = this.getBukkitEntity();
var handle = (ServerPlayer) ((CraftEntity) entity).getHandle();
if (!isChunkLoaded(spawnLocation)) {
spawnLocation.getChunk().load();
}
handle.level().addFreshEntity(handle, CreatureSpawnEvent.SpawnReason.CUSTOM);
((CraftServer) Bukkit.getServer()).getHandle().respawn(
this,
true,
PlayerRespawnEvent.RespawnReason.PLUGIN
);
// move directly
getBukkitEntity().teleport(spawnLocation);
}
}

View File

@ -1,8 +1,15 @@
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;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import properties.FakeplayerProperties;
@ -21,8 +28,8 @@ public class FakePlayerManager {
private final FakeplayerProperties properties = FakeplayerProperties.instance;
public synchronized void spawnFakePlayer(
@NotNull Player creator,
@NotNull Location location
@NotNull CommandSender creator,
@NotNull Location at
) {
var existed = getFakePlayers(creator).size();
if (!creator.isOp() && existed >= properties.getMaximum()) {
@ -36,22 +43,26 @@ public class FakePlayerManager {
name = name.substring(0, (16 - suffix.length()));
}
name = name + suffix;
FakePlayerSpawner.spawn(
var player = new FakePlayer(
((CraftServer) Bukkit.getServer()).getServer(),
((CraftWorld) at.getWorld()).getHandle(),
UUID.randomUUID(),
name,
creator,
location
at
);
var p = player.prepare();
p.setMetadata(FakePlayerSpawner.META_KEY_CREATOR, new FixedMetadataValue(Main.getInstance(), creator.getName()));
}
public @Nullable Player getFakePlayer(@NotNull Player creator, @NotNull String name) {
public @Nullable Player getFakePlayer(@NotNull CommandSender creator, @NotNull String name) {
var fake = getFakePlayer(name);
if (fake == null) {
return null;
}
var c = getCreator(fake);
if (c == null || !c.equals(creator.getUniqueId())) {
if (c == null || !c.equals(creator.getName())) {
return null;
}
@ -79,13 +90,13 @@ public class FakePlayerManager {
return fakes.size();
}
public @Nullable UUID getCreator(@NotNull Player fakePlayer) {
public @Nullable String getCreator(@NotNull Player fakePlayer) {
var meta = fakePlayer.getMetadata(FakePlayerSpawner.META_KEY_CREATOR);
if (meta.isEmpty()) {
return null;
}
return UUID.fromString(meta.get(0).asString());
return meta.get(0).asString();
}
public int removeFakePlayers() {
@ -105,15 +116,15 @@ public class FakePlayerManager {
.collect(Collectors.toList());
}
public @NotNull List<Player> getFakePlayers(@NotNull Player creator) {
var creatorUniqueId = creator.getUniqueId().toString();
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)
.stream()
.anyMatch(meta -> meta.asString().equals(creatorUniqueId)))
.anyMatch(meta -> meta.asString().equals(name)))
.collect(Collectors.toList());
}

View File

@ -27,73 +27,73 @@ 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
}
}
// 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
// }
// }
}