支持 1.20.3 和 1.20.4

This commit is contained in:
tanyaofei 2024-03-18 14:12:41 +08:00
parent 3827ac6e96
commit f73cad74cb
21 changed files with 1277 additions and 6 deletions

View File

@ -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()
));

View File

@ -39,6 +39,11 @@
<artifactId>fakeplayer-v1_20_R2</artifactId>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-v1_20_R4</artifactId>
</dependency>
</dependencies>
<build>
@ -77,13 +82,13 @@
<id>copy</id>
<phase>package</phase>
<configuration>
<tasks>
<target>
<copy todir="../server/plugins">
<fileset dir="${project.build.directory}">
<include name="fakeplayer-${plugin.version}.jar"/>
</fileset>
</copy>
</tasks>
</target>
</configuration>
<goals>
<goal>run</goal>

View File

@ -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

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>fakeplayer-v1_20_R4</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
<classifier>remapped-mojang</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>
org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-mojang
</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>
${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
</inputFile>
<srgIn>org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-obf
</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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() {
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<Entity> 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);
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}

View File

@ -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();
};
}
}
}

View File

@ -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<String> 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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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
));
}
}

11
pom.xml
View File

@ -13,15 +13,16 @@
<modules>
<module>fakeplayer-api</module>
<module>fakeplayer-core</module>
<module>fakeplayer-dist</module>
<module>fakeplayer-v1_20_R1</module>
<module>fakeplayer-v1_20_R2</module>
<module>fakeplayer-dist</module>
<module>fakeplayer-v1_20_R4</module>
</modules>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<plugin.version>0.2.8</plugin.version>
<plugin.version>0.2.9</plugin.version>
</properties>
<repositories>
@ -107,6 +108,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-v1_20_R4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.jikoo.OpenInv</groupId>
<artifactId>openinvapi</artifactId>