diff --git a/fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/listener/FakeplayerListener.java b/fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/listener/FakeplayerListener.java index aab3761..eaf9621 100644 --- a/fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/listener/FakeplayerListener.java +++ b/fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/listener/FakeplayerListener.java @@ -6,6 +6,7 @@ import io.github.hello09x.fakeplayer.core.Main; import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig; import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager; import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository; +import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator; import org.bukkit.Bukkit; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; @@ -41,7 +42,7 @@ public class FakeplayerListener implements Listener { public void onLogin(@NotNull PlayerLoginEvent event) { var player = event.getPlayer(); - if (event.getPlayer().hasMetadata("fakeplayer.dummy")) { + if (InternalAddressGenerator.canBeGenerated(event.getAddress())) { return; } @@ -52,7 +53,7 @@ public class FakeplayerListener implements Listener { newline(), text("<<---- fakeplayer ---->>", GRAY) )); - log.info("%s(%s) was refused to login cause his UUID was used by fake player".formatted( + log.info("%s(%s) was refused to login cause his UUID was used by [Fakeplayer]".formatted( player.getName(), player.getUniqueId() )); diff --git a/fakeplayer-dist/pom.xml b/fakeplayer-dist/pom.xml index 168165f..19bcd89 100644 --- a/fakeplayer-dist/pom.xml +++ b/fakeplayer-dist/pom.xml @@ -39,6 +39,11 @@ fakeplayer-v1_20_R2 + + io.github.hello09x.fakeplayer + fakeplayer-v1_20_R4 + + @@ -77,13 +82,13 @@ copy package - + - + run diff --git a/fakeplayer-dist/src/main/resources/META-INF/services/io.github.hello09x.fakeplayer.api.spi.NMSBridge b/fakeplayer-dist/src/main/resources/META-INF/services/io.github.hello09x.fakeplayer.api.spi.NMSBridge index 16915de..b295c80 100644 --- a/fakeplayer-dist/src/main/resources/META-INF/services/io.github.hello09x.fakeplayer.api.spi.NMSBridge +++ b/fakeplayer-dist/src/main/resources/META-INF/services/io.github.hello09x.fakeplayer.api.spi.NMSBridge @@ -1,2 +1,3 @@ io.github.hello09x.fakeplayer.v1_20_R1.spi.NMSBridgeImpl io.github.hello09x.fakeplayer.v1_20_R2.spi.NMSBridgeImpl +io.github.hello09x.fakeplayer.v1_20_R4.spi.NMSBridgeImpl diff --git a/fakeplayer-v1_20_R4/pom.xml b/fakeplayer-v1_20_R4/pom.xml new file mode 100644 index 0000000..9d9a26e --- /dev/null +++ b/fakeplayer-v1_20_R4/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + + io.github.hello09x.fakeplayer + fakeplayer-parent + 1.0.0 + + + fakeplayer-v1_20_R4 + + + 17 + 17 + UTF-8 + + + + + io.papermc.paper + paper-api + provided + + + + io.github.hello09x.fakeplayer + fakeplayer-core + provided + + + + io.github.hello09x.fakeplayer + fakeplayer-api + provided + + + + org.spigotmc + spigot + 1.20.4-R0.1-SNAPSHOT + remapped-mojang + provided + + + + org.projectlombok + lombok + provided + + + + + + + + net.md-5 + specialsource-maven-plugin + 1.2.4 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:txt:maps-mojang + true + + org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-mojang + + true + remapped-obf + + + + package + + remap + + remap-spigot + + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + + org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + + \ No newline at end of file diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/AttackAction.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/AttackAction.java new file mode 100644 index 0000000..3b3d212 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/AttackAction.java @@ -0,0 +1,49 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.action; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; + + +public class AttackAction extends TraceAction { + + private final ServerPlayer player; + + public AttackAction(ServerPlayer player) { + super(player); + this.player = player; + } + + + @Override + public boolean tick() { + var hit = this.getTarget(); + if (hit == null) { + return false; + } + + if (hit.getType() != HitResult.Type.ENTITY) { + return false; + } + + var entityHit = (EntityHitResult) hit; + player.attack(entityHit.getEntity()); + player.swing(InteractionHand.MAIN_HAND); + player.resetAttackStrengthTicker(); + player.resetLastActionTime(); + return true; + } + + @Override + public void inactiveTick() { + + } + + @Override + public void stop() { + + } + + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/MineAction.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/MineAction.java new file mode 100644 index 0000000..4cc66df --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/MineAction.java @@ -0,0 +1,160 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.action; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import org.jetbrains.annotations.Nullable; + +import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*; + +public class MineAction extends TraceAction { + + private final Current current = new Current(); + + public MineAction(ServerPlayer player) { + super(player); + } + + @Override + @SuppressWarnings("resource") + public boolean tick() { + var hit = this.getTarget(); + if (hit == null) { + return false; + } + + if (hit.getType() != HitResult.Type.BLOCK) { + return false; + } + + if (current.freeze > 0) { + current.freeze--; + return false; + } + + var blockHit = (BlockHitResult) hit; + var pos = blockHit.getBlockPos(); + var side = blockHit.getDirection(); + + if (player.blockActionRestricted(player.level(), pos, player.gameMode.getGameModeForPlayer())) { + return false; + } + + if (current.pos != null && player.level().getBlockState(current.pos).isAir()) { + current.pos = null; + return false; + } + + var state = player.level().getBlockState(pos); + var broken = false; + if (player.gameMode.getGameModeForPlayer().isCreative()) { + player.gameMode.handleBlockBreakAction( + pos, + START_DESTROY_BLOCK, + side, + player.level().getMaxBuildHeight(), + -1 + ); + current.freeze = 5; + broken = true; + } else if (current.pos == null || !current.pos.equals(pos)) { + if (current.pos != null) { + player.gameMode.handleBlockBreakAction( + current.pos, + ABORT_DESTROY_BLOCK, + side, + player.level().getMaxBuildHeight(), + -1 + ); + } + + player.gameMode.handleBlockBreakAction( + pos, + START_DESTROY_BLOCK, + side, + player.level().getMaxBuildHeight(), + -1 + ); + + if (!state.isAir() && current.progress == 0) { + state.attack(player.level(), pos, player); + } + + if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) { + current.pos = null; + broken = true; + } else { + current.pos = pos; + current.progress = 0; + } + } else { + current.progress += state.getDestroyProgress(player, player.level(), pos); + if (current.progress >= 1) { + player.gameMode.handleBlockBreakAction( + pos, + STOP_DESTROY_BLOCK, + side, + player.level().getMaxBuildHeight(), + -1 + ); + current.pos = null; + current.freeze = 5; + broken = true; + } + player.level().destroyBlockProgress(-1, pos, (int) (current.progress * 10)); + } + + player.resetLastActionTime(); + player.swing(InteractionHand.MAIN_HAND); + return broken; + } + + @Override + public void inactiveTick() { + stop(); + } + + @Override + @SuppressWarnings("resource") + public void stop() { + if (current.pos == null) { + return; + } + + player.level().destroyBlockProgress(-1, current.pos, -1); + player.gameMode.handleBlockBreakAction( + current.pos, + ABORT_DESTROY_BLOCK, + Direction.DOWN, + player.level().getMaxBuildHeight(), + -1 + ); + current.pos = null; + current.freeze = 0; + current.progress = 0; + } + + private static class Current { + + /** + * 当前左键的目标位置 + */ + @Nullable + public BlockPos pos; + + /** + * 破坏方块的进度 + */ + public float progress; + + /** + * 冷却, 单位: tick + */ + public int freeze; + + } + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/TraceAction.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/TraceAction.java new file mode 100644 index 0000000..02afe92 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/TraceAction.java @@ -0,0 +1,24 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.action; + +import io.github.hello09x.fakeplayer.api.spi.Action; +import io.github.hello09x.fakeplayer.v1_20_R4.action.util.Tracer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.phys.HitResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class TraceAction implements Action { + + protected final ServerPlayer player; + + protected TraceAction(@NotNull ServerPlayer player) { + this.player = player; + } + + protected @Nullable HitResult getTarget() { + double reach = player.gameMode.isCreative() ? 5 : 4.5f; + return Tracer.rayTrace(player, 1, reach, false); + } + + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/UseAction.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/UseAction.java new file mode 100644 index 0000000..b11fec2 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/UseAction.java @@ -0,0 +1,98 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.action; + +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.decoration.ItemFrame; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import org.jetbrains.annotations.NotNull; + +public class UseAction extends TraceAction { + + private final Current current = new Current(); + + public UseAction(@NotNull ServerPlayer player) { + super(player); + } + + @Override + @SuppressWarnings("resource") + public boolean tick() { + if (current.freeze > 0) { + current.freeze--; + return false; + } + + if (player.isUsingItem()) { + return true; + } + + var hit = this.getTarget(); + if (hit == null) { + return false; + } + + for (var hand : InteractionHand.values()) { + switch (hit.getType()) { + case BLOCK -> { + player.resetLastActionTime(); + var world = player.serverLevel(); + var blockHit = (BlockHitResult) hit; + var pos = blockHit.getBlockPos(); + var side = blockHit.getDirection(); + if (pos.getY() < player.level().getMaxBuildHeight() - (side == Direction.UP ? 1 : 0) && world.mayInteract(player, pos)) { + var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit); + if (result.consumesAction()) { + player.swing(hand); + current.freeze = 3; + return true; + } + } + } + case ENTITY -> { + player.resetLastActionTime(); + var entityHit = (EntityHitResult) hit; + var entity = entityHit.getEntity(); + boolean handWasEmpty = player.getItemInHand(hand).isEmpty(); + 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()) { + current.freeze = 3; + return true; + } + if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) { + current.freeze = 3; + return true; + } + } + } + var handItem = player.getItemInHand(hand); + if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) { + player.resetLastActionTime(); + current.freeze = 3; + return true; + } + } + return false; + } + + @Override + public void inactiveTick() { + this.stop(); + } + + @Override + public void stop() { + current.freeze = 0; + player.releaseUsingItem(); + } + + private final static class Current { + + /** + * 冷却, 单位: tick + */ + public int freeze; + } +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/util/Tracer.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/util/Tracer.java new file mode 100644 index 0000000..782a52e --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/action/util/Tracer.java @@ -0,0 +1,105 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.action.util; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.phys.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Predicate; + +/** + * copy from fabric carpet mod + */ +public class Tracer { + + public static @Nullable HitResult rayTrace( + @NotNull Entity source, + float partialTicks, + double reach, + boolean fluids + ) { + var blockHit = rayTraceBlocks(source, partialTicks, reach, fluids); + double maxSqDist = reach * reach; + if (blockHit != null) { + maxSqDist = blockHit.getLocation().distanceToSqr(source.getEyePosition(partialTicks)); + } + EntityHitResult entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist); + return entityHit == null ? blockHit : entityHit; + } + + @SuppressWarnings("resource") + public static @Nullable BlockHitResult rayTraceBlocks( + @NotNull Entity source, + float partialTicks, + double reach, + boolean fluids + ) { + var pos = source.getEyePosition(partialTicks); + var rotation = source.getViewVector(partialTicks); + var reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach); + return source.level().clip(new ClipContext(pos, reachEnd, ClipContext.Block.OUTLINE, fluids ? + ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, source)); + } + + public static @Nullable EntityHitResult rayTraceEntities( + @NotNull Entity source, + float partialTicks, + double reach, + double maxSqDist + ) { + var pos = source.getEyePosition(partialTicks); + var reachVec = source.getViewVector(partialTicks).scale(reach); + var box = source.getBoundingBox().expandTowards(reachVec).inflate(1); + return rayTraceEntities(source, + pos, + pos.add(reachVec), + box, + e -> !e.isSpectator() && e.isPickable(), + maxSqDist); + } + + public static @Nullable EntityHitResult rayTraceEntities( + @NotNull Entity source, + @NotNull Vec3 start, + @NotNull Vec3 end, + @NotNull AABB box, + @NotNull Predicate predicate, + double maxSqDistance + ) { + @SuppressWarnings("resource") + var world = source.level(); + double targetDistance = maxSqDistance; + Entity target = null; + Vec3 targetHitPos = null; + for (Entity current : world.getEntities(source, box, predicate)) { + var currentBox = current.getBoundingBox().inflate(current.getPickRadius()); + var currentHit = currentBox.clip(start, end); + if (currentBox.contains(start)) { + if (targetDistance >= 0) { + target = current; + targetHitPos = currentHit.orElse(start); + targetDistance = 0; + } + } else if (currentHit.isPresent()) { + var currentHitPos = currentHit.get(); + var currentDistance = start.distanceToSqr(currentHitPos); + if (currentDistance < targetDistance || targetDistance == 0) { + if (current.getRootVehicle() == source.getRootVehicle()) { + if (targetDistance == 0) { + target = current; + targetHitPos = currentHitPos; + } + } + else + { + target = current; + targetHitPos = currentHitPos; + targetDistance = currentDistance; + } + } + } + } + return target == null ? null : new EntityHitResult(target, targetHitPos); + } +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyChannel.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyChannel.java new file mode 100644 index 0000000..8bee8a2 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyChannel.java @@ -0,0 +1,99 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.network; + +import io.netty.channel.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +public class DummyChannel extends AbstractChannel { + private final static EventLoop EVENT_LOOP = new DefaultEventLoop(); + private final ChannelConfig config = new DefaultChannelConfig(this); + + private final InetAddress address; + + public DummyChannel(@Nullable Channel parent, @NotNull InetAddress address) { + super(parent); + this.address = address; + } + + @Override + public ChannelConfig config() { + config.setAutoRead(true); + return config; + } + + @Override + protected void doBeginRead() throws Exception { + } + + @Override + protected void doBind(SocketAddress arg0) throws Exception { + } + + @Override + protected void doClose() throws Exception { + } + + @Override + protected void doDisconnect() throws Exception { + } + + @Override + protected void doWrite(ChannelOutboundBuffer in) throws Exception { + for (;;) { + Object msg = in.current(); + if (msg == null) { + break; + } + in.remove(); + } + } + + @Override + public boolean isActive() { + return true; + } + + @Override + protected boolean isCompatible(EventLoop arg0) { + return true; + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + protected SocketAddress localAddress0() { + return new InetSocketAddress(address, 25565); + } + + @Override + public ChannelMetadata metadata() { + return new ChannelMetadata(true); + } + + @Override + protected AbstractUnsafe newUnsafe() { + return new AbstractUnsafe() { + @Override + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + safeSetSuccess(promise); + } + }; + } + + @Override + protected SocketAddress remoteAddress0() { + return new InetSocketAddress(address, 25565); + } + + @Override + public EventLoop eventLoop() { + return EVENT_LOOP; + } +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyConnection.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyConnection.java new file mode 100644 index 0000000..e8ab138 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyConnection.java @@ -0,0 +1,38 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.network; + +import net.minecraft.network.Connection; +import net.minecraft.network.ConnectionProtocol; +import net.minecraft.network.PacketSendListener; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.PacketFlow; +import org.jetbrains.annotations.NotNull; + +import java.net.InetAddress; + +public class DummyConnection extends Connection { + public DummyConnection(@NotNull InetAddress address) { + super(PacketFlow.SERVERBOUND); + this.channel = new DummyChannel(null, address); + this.address = this.channel.remoteAddress(); + Connection.configureSerialization(this.channel.pipeline(), PacketFlow.SERVERBOUND, null); + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public void send(Packet packet, PacketSendListener listener) { + } + + @Override + public void send(Packet packet) { + } + + public void setProtocolAttr(@NotNull ConnectionProtocol protocol) { + this.channel.attr(Connection.ATTRIBUTE_SERVERBOUND_PROTOCOL).set(protocol.codec(PacketFlow.SERVERBOUND)); + this.channel.attr(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL).set(protocol.codec(PacketFlow.CLIENTBOUND)); + } + +} \ No newline at end of file diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyPlayerAdvancements.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyPlayerAdvancements.java new file mode 100644 index 0000000..02b9b81 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyPlayerAdvancements.java @@ -0,0 +1,63 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.network; + +import com.mojang.datafixers.DataFixer; +import net.minecraft.advancements.AdvancementHolder; +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.nio.file.Path; + +public class DummyPlayerAdvancements extends PlayerAdvancements { + + public DummyPlayerAdvancements( + DataFixer datafixer, + PlayerList playerlist, + ServerAdvancementManager manager, + Path path, + ServerPlayer player + ) { + super(datafixer, playerlist, manager, path, player); + this.save(); + } + + @Override + public boolean award(AdvancementHolder advancementholder, String s) { + return false; + } + + @Override + public void flushDirty(ServerPlayer player) { + } + + @Override + public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) { + return new AdvancementProgress(); + } + + @Override + public boolean revoke(AdvancementHolder advancement, String s) { + return false; + } + + @Override + public void save() { + } + + @Override + public void setPlayer(ServerPlayer player) { + } + + @Override + public void setSelectedTab(AdvancementHolder advancement) { + } + + @Override + public void stopListening() { + + } + + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyServerGamePacketListenerImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyServerGamePacketListenerImpl.java new file mode 100644 index 0000000..a9445ba --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/network/DummyServerGamePacketListenerImpl.java @@ -0,0 +1,69 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.network; + +import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener; +import io.github.hello09x.fakeplayer.core.Main; +import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager; +import io.netty.buffer.Unpooled; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.CommonListenerCookie; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl implements NMSServerGamePacketListener { + + private final static FakeplayerManager manager = FakeplayerManager.instance; + + public DummyServerGamePacketListenerImpl( + @NotNull MinecraftServer server, + @NotNull Connection connection, + @NotNull ServerPlayer player, + @NotNull CommonListenerCookie cookie + ) { + super(server, connection, player, cookie); + Optional.ofNullable(Bukkit.getPlayer(player.getUUID())) + .map(CraftPlayer.class::cast) + .ifPresent(p -> p.addChannel(StandardMessenger.validateAndCorrectChannel(BUNGEE_CORD_CHANNEL))); + } + + @Override + public void send(Packet packet) { + if (packet instanceof ClientboundCustomPayloadPacket p) { + this.handleCustomPayloadPacket(p.payload()); + } + } + + private void handleCustomPayloadPacket(@NotNull CustomPacketPayload payload) { + var channel = payload.id().getNamespace() + ":" + payload.id().getPath(); + if (!channel.equals(BUNGEE_CORD_CHANNEL)) { + return; + } + + var recipient = Bukkit + .getOnlinePlayers() + .stream() + .filter(manager::notContains) + .findAny() + .orElse(null); + if (recipient == null) { + return; + } + + var buf = new FriendlyByteBuf(Unpooled.buffer(0, 1048576)); + payload.write(buf); + var message = buf.array(); + + recipient.sendPluginMessage(Main.getInstance(), channel, message); + } + +} \ No newline at end of file diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/ActionTickerImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/ActionTickerImpl.java new file mode 100644 index 0000000..17abf06 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/ActionTickerImpl.java @@ -0,0 +1,29 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + + +import io.github.hello09x.fakeplayer.api.spi.Action; +import io.github.hello09x.fakeplayer.api.spi.ActionTicker; +import io.github.hello09x.fakeplayer.core.entity.action.BaseActionTicker; +import io.github.hello09x.fakeplayer.v1_20_R4.action.AttackAction; +import io.github.hello09x.fakeplayer.v1_20_R4.action.MineAction; +import io.github.hello09x.fakeplayer.v1_20_R4.action.UseAction; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class ActionTickerImpl extends BaseActionTicker implements ActionTicker { + + public ActionTickerImpl(@NotNull Player player, @NotNull Action.ActionType action, @NotNull Action.ActionSetting setting) { + super(player, action, setting); + if (this.action == null) { + this.action = switch (action) { + case ATTACK -> new AttackAction(((CraftPlayer) player).getHandle()); + case MINE -> new MineAction(((CraftPlayer) player).getHandle()); + case USE -> new UseAction(((CraftPlayer) player).getHandle()); + case JUMP, LOOK_AT_NEAREST_ENTITY, DROP_INVENTORY, DROP_STACK, DROP_ITEM -> + throw new UnsupportedOperationException(); + }; + } + } + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSBridgeImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSBridgeImpl.java new file mode 100644 index 0000000..c84f4d4 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSBridgeImpl.java @@ -0,0 +1,53 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import io.github.hello09x.fakeplayer.api.spi.*; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.net.InetAddress; +import java.util.Set; + +public class NMSBridgeImpl implements NMSBridge { + + private final static Set SUPPORTS = Set.of("1.20.3", "1.20.4"); + + @Override + public @NotNull NMSEntity fromEntity(@NotNull Entity entity) { + return new NMSEntityImpl(entity); + } + + @Override + public @NotNull NMSServer fromServer(@NotNull Server server) { + return new NMSServerImpl(server); + } + + @Override + public @NotNull NMSServerLevel fromWorld(@NotNull World world) { + return new NMSServerLevelImpl(world); + } + + @Override + public @NotNull NMSServerPlayer fromPlayer(@NotNull Player player) { + return new NMSServerPlayerImpl(player); + } + + @Override + public @NotNull NMSNetwork createNetwork(@NotNull InetAddress address) { + return new NMSNetworkImpl(address); + } + + @Override + public boolean isSupported() { + return SUPPORTS.contains(Bukkit.getMinecraftVersion()); + } + + @Override + public @NotNull ActionTicker createAction(@NotNull Player player, @NotNull Action.ActionType action, @NotNull Action.ActionSetting setting) { + return new ActionTickerImpl(player, action, setting); + } + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSEntityImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSEntityImpl.java new file mode 100644 index 0000000..49687fb --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSEntityImpl.java @@ -0,0 +1,19 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import io.github.hello09x.fakeplayer.api.spi.NMSEntity; +import lombok.Getter; +import net.minecraft.world.entity.Entity; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity; +import org.jetbrains.annotations.NotNull; + +public class NMSEntityImpl implements NMSEntity { + + @Getter + private final Entity handle; + + public NMSEntityImpl(@NotNull org.bukkit.entity.@NotNull Entity entity) { + this.handle = ((CraftEntity) entity).getHandle(); + } + + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSNetworkImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSNetworkImpl.java new file mode 100644 index 0000000..43ced5f --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSNetworkImpl.java @@ -0,0 +1,64 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import io.github.hello09x.fakeplayer.api.spi.NMSNetwork; +import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener; +import io.github.hello09x.fakeplayer.v1_20_R4.network.DummyConnection; +import io.github.hello09x.fakeplayer.v1_20_R4.network.DummyServerGamePacketListenerImpl; +import net.minecraft.network.ConnectionProtocol; +import net.minecraft.server.network.CommonListenerCookie; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.net.InetAddress; + +public class NMSNetworkImpl implements NMSNetwork { + + @NotNull + private final DummyConnection connection; + + private NMSServerGamePacketListener serverGamePacketListener; + + public NMSNetworkImpl( + @NotNull InetAddress address + ) { + this.connection = new DummyConnection(address); + } + + @NotNull + @Override + public NMSServerGamePacketListener placeNewPlayer( + @NotNull Server server, + @NotNull Player player + ) { + this.connection.setProtocolAttr(ConnectionProtocol.PLAY); + var handle = ((CraftPlayer) player).getHandle(); + var cookie = CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile()); + var listener = new DummyServerGamePacketListenerImpl( + ((CraftServer) server).getServer(), + this.connection, + handle, + cookie + ); + handle.connection = listener; + ((CraftServer) server).getHandle().placeNewPlayer( + this.connection, + handle, + cookie + ); + this.serverGamePacketListener = listener; + return listener; + } + + @NotNull + @Override + public NMSServerGamePacketListener getServerGamePacketListener() throws IllegalStateException { + if (this.serverGamePacketListener == null) { + throw new IllegalStateException("not initialized"); + } + return this.serverGamePacketListener; + } + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerImpl.java new file mode 100644 index 0000000..99c8d84 --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerImpl.java @@ -0,0 +1,38 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import com.mojang.authlib.GameProfile; +import io.github.hello09x.bedrock.util.Worlds; +import io.github.hello09x.fakeplayer.api.spi.NMSServer; +import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer; +import lombok.Getter; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class NMSServerImpl implements NMSServer { + + + @Getter + private final MinecraftServer handle; + + public NMSServerImpl(@NotNull Server server) { + this.handle = ((CraftServer) server).getServer(); + } + + @Override + public @NotNull NMSServerPlayer newPlayer(@NotNull UUID uuid, @NotNull String name) { + var handle = new ServerPlayer( + new NMSServerImpl(Bukkit.getServer()).getHandle(), + new NMSServerLevelImpl(Worlds.getMainWorld()).getHandle(), + new GameProfile(uuid, name), + ClientInformation.createDefault() + ); + return new NMSServerPlayerImpl(handle.getBukkitEntity()); + } +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerLevelImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerLevelImpl.java new file mode 100644 index 0000000..93bb71d --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerLevelImpl.java @@ -0,0 +1,19 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import io.github.hello09x.fakeplayer.api.spi.NMSServerLevel; +import lombok.Getter; +import net.minecraft.server.level.ServerLevel; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.jetbrains.annotations.NotNull; + +public class NMSServerLevelImpl implements NMSServerLevel { + + @Getter + private final ServerLevel handle; + + public NMSServerLevelImpl(@NotNull World world) { + this.handle = ((CraftWorld) world).getHandle(); + } + +} diff --git a/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerPlayerImpl.java b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerPlayerImpl.java new file mode 100644 index 0000000..02c58ba --- /dev/null +++ b/fakeplayer-v1_20_R4/src/main/java/io/github/hello09x/fakeplayer/v1_20_R4/spi/NMSServerPlayerImpl.java @@ -0,0 +1,232 @@ +package io.github.hello09x.fakeplayer.v1_20_R4.spi; + +import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer; +import io.github.hello09x.fakeplayer.core.constant.ConstantPool; +import io.github.hello09x.fakeplayer.core.util.Reflections; +import io.github.hello09x.fakeplayer.v1_20_R4.network.DummyPlayerAdvancements; +import lombok.Getter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ServerboundClientCommandPacket; +import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; +import net.minecraft.server.PlayerAdvancements; +import net.minecraft.server.level.ClientInformation; +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.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; + +public class NMSServerPlayerImpl implements NMSServerPlayer { + + private final static Field ServerPlayer$advancements = Reflections.getFirstFieldByType( + ServerPlayer.class, + PlayerAdvancements.class, + false + ); + + @Getter + private final ServerPlayer handle; + + @Getter + private final CraftPlayer player; + + public NMSServerPlayerImpl(@NotNull Player player) { + this.player = ((CraftPlayer) player); + this.handle = ((CraftPlayer) player).getHandle(); + } + + @Override + public double getX() { + return handle.getX(); + } + + @Override + public double getY() { + return handle.getY(); + } + + @Override + public double getZ() { + return handle.getZ(); + } + + @Override + public void setXo(double xo) { + handle.xo = xo; + } + + @Override + public void setYo(double yo) { + handle.yo = yo; + } + + @Override + public void setZo(double zo) { + handle.zo = zo; + } + + @Override + public void doTick() { + handle.doTick(); + ; + } + + @Override + public void absMoveTo(double x, double y, double z, float yRot, float xRot) { + handle.absMoveTo(x, y, z, yRot, xRot); + } + + @Override + public float getYRot() { + return handle.getYRot(); + } + + @Override + public void setYRot(float yRot) { + handle.setYRot(yRot); + } + + @Override + public float getXRot() { + return handle.getXRot(); + } + + @Override + public void setXRot(float xRot) { + handle.setXRot(xRot); + } + + @Override + public void setZza(float zza) { + handle.zza = zza; + } + + @Override + public void setXxa(float xxa) { + handle.xxa = xxa; + } + + @Override + public boolean startRiding(@NotNull Entity entity, boolean force) { + return handle.startRiding(new NMSEntityImpl(entity).getHandle(), force); + } + + @Override + public void stopRiding() { + handle.stopRiding(); + } + + + @Override + public int getTickCount() { + return handle.tickCount; + } + + @Override + public void drop(boolean allStack) { + handle.drop(allStack); + } + + @Override + public void resetLastActionTime() { + handle.resetLastActionTime(); + } + + @Override + public boolean onGround() { + return handle.onGround(); + } + + @Override + public void jumpFromGround() { + handle.jumpFromGround(); + } + + @Override + public void setJumping(boolean jumping) { + handle.setJumping(jumping); + } + + @Override + public boolean isUsingItem() { + return handle.isUsingItem(); + } + + @Override + public void disableAdvancements(@NotNull Plugin plugin) { + if (ServerPlayer$advancements == null) { + return; + } + + var server = ((CraftServer) Bukkit.getServer()).getServer(); + try { + ServerPlayer$advancements.set( + handle, + new DummyPlayerAdvancements( + server.getFixerUpper(), + server.getPlayerList(), + server.getAdvancements(), + plugin.getDataFolder().getParentFile().toPath(), + handle + ) + ); + } catch (IllegalAccessException ignored) { + } + } + + @Override + public void drop(int slot, boolean flag, boolean flag1) { + var inventory = handle.getInventory(); + handle.drop(inventory.removeItem(slot, inventory.getItem(slot).getCount()), flag, flag1); + } + + @Override + public void setPlayBefore() { + player.readExtraData(new CompoundTag()); + } + + @Override + public void setupClientOptions() { + var option = new ClientInformation( + "en_us", + Bukkit.getViewDistance(), + ChatVisiblity.SYSTEM, + false, + ConstantPool.MODEL_CUSTOMISATION, + HumanoidArm.RIGHT, + false, + true + ); + + handle.updateOptions(option); + } + + @Override + public void respawn() { + if (!this.player.isDead()) { + return; + } + + var packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN); + handle.connection.handleClientCommand(packet); + } + + @Override + public void swapItemWithOffhand() { + handle.connection.handlePlayerAction(new ServerboundPlayerActionPacket( + ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND, + new BlockPos(0, 0, 0), + Direction.DOWN + )); + } + +} diff --git a/pom.xml b/pom.xml index a11e106..bc4aa74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,15 +13,16 @@ fakeplayer-api fakeplayer-core + fakeplayer-dist fakeplayer-v1_20_R1 fakeplayer-v1_20_R2 - fakeplayer-dist + fakeplayer-v1_20_R4 17 UTF-8 - 0.2.8 + 0.2.9 @@ -107,6 +108,12 @@ ${project.version} + + io.github.hello09x.fakeplayer + fakeplayer-v1_20_R4 + ${project.version} + + com.github.jikoo.OpenInv openinvapi