Minestom/src/main/java/net/minestom/server/entity/fakeplayer/FakePlayerController.java

210 lines
7.9 KiB
Java

package net.minestom.server.entity.fakeplayer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.client.ClientPacket;
import net.minestom.server.network.packet.client.play.*;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.network.packet.server.play.PlayerPositionAndLookPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* This class acts as a client controller for {@link FakePlayer}.
* <p>
* The main use is to simulate the receiving of {@link ClientPacket}
*/
public class FakePlayerController {
private final FakePlayer fakePlayer;
/**
* Initializes a new {@link FakePlayerController} with the given {@link FakePlayer}.
*
* @param fakePlayer The fake player that should used the controller.
*/
public FakePlayerController(@NotNull FakePlayer fakePlayer) {
this.fakePlayer = fakePlayer;
}
/**
* Simulates a click in a window.
*
* @param playerInventory {@code true} if the window a {@link PlayerInventory}, otherwise {@code false}.
* @param slot The slot where the fake player should click on.
* @param button The mouse button that the fake player should used.
* @param clickType The click type
*/
public void clickWindow(boolean playerInventory, short slot, byte button, ClientClickWindowPacket.ClickType clickType) {
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
AbstractInventory abstractInventory = inventory == null ? fakePlayer.getInventory() : inventory;
playerInventory = abstractInventory instanceof PlayerInventory;
slot = playerInventory ? (short) PlayerInventoryUtils.convertToPacketSlot(slot) : slot;
ItemStack itemStack = abstractInventory.getItemStack(slot);
addToQueue(new ClientClickWindowPacket(playerInventory ? 0 : inventory.getWindowId(), 0,
slot, button, clickType,
List.of(), itemStack));
}
/**
* Closes the current opened inventory if there is any.
*/
public void closeWindow() {
Inventory openInventory = fakePlayer.getOpenInventory();
addToQueue(new ClientCloseWindowPacket(openInventory == null ? 0 : openInventory.getWindowId()));
}
/**
* Sends a plugin message to the player.
*
* @param channel The channel of the message.
* @param message The message data.
*/
public void sendPluginMessage(String channel, byte[] message) {
addToQueue(new ClientPluginMessagePacket(channel, message));
}
/**
* Sends a plugin message to the player.
*
* @param channel The channel of the message.
* @param message The message data.
*/
public void sendPluginMessage(String channel, String message) {
sendPluginMessage(channel, message.getBytes());
}
/**
* Attacks the given {@code entity}.
*
* @param entity The entity that is to be attacked.
*/
public void attackEntity(Entity entity) {
addToQueue(new ClientInteractEntityPacket(entity.getEntityId(), new ClientInteractEntityPacket.Attack(), fakePlayer.isSneaking()));
}
/**
* Respawns the player.
*
* @see Player#respawn()
*/
public void respawn() {
// Sending the respawn packet for some reason
// Is related to FakePlayer#showPlayer and the tablist option (probably because of the scheduler)
/*ClientStatusPacket statusPacket = new ClientStatusPacket();
statusPacket.action = ClientStatusPacket.Action.PERFORM_RESPAWN;
addToQueue(statusPacket);*/
fakePlayer.respawn();
}
/**
* Changes the current held slot for the player.
*
* @param slot The slot that the player has to held.
* @throws IllegalArgumentException If {@code slot} is not between {@code 0} and {@code 8}.
*/
public void setHeldItem(short slot) {
Check.argCondition(!MathUtils.isBetween(slot, 0, 8), "Slot has to be between 0 and 8!");
addToQueue(new ClientHeldItemChangePacket(slot));
}
/**
* Sends an animation packet that animates the specified arm.
*
* @param hand The hand of the arm to be animated.
*/
public void sendArmAnimation(Player.Hand hand) {
addToQueue(new ClientAnimationPacket(hand));
}
/**
* Uses the item in the given {@code hand}.
*
* @param hand The hand in which an ite mshould be.
*/
public void useItem(Player.Hand hand) {
addToQueue(new ClientUseItemPacket(hand, 0));
}
/**
* Rotates the fake player.
*
* @param yaw The new yaw for the fake player.
* @param pitch The new pitch for the fake player.
*/
public void rotate(float yaw, float pitch) {
addToQueue(new ClientPlayerRotationPacket(yaw, pitch, fakePlayer.isOnGround()));
}
/**
* Starts the digging process of the fake player.
*
* @param blockPosition The position of the block to be excavated.
* @param blockFace From where the block is struck.
*/
public void startDigging(Point blockPosition, BlockFace blockFace) {
addToQueue(new ClientPlayerDiggingPacket(ClientPlayerDiggingPacket.Status.STARTED_DIGGING, blockPosition, blockFace, 0));
}
/**
* Stops the digging process of the fake player.
*
* @param blockPosition The position of the block to be excavated.
* @param blockFace From where the block is struck.
*/
public void stopDigging(Point blockPosition, BlockFace blockFace) {
addToQueue(new ClientPlayerDiggingPacket(ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING, blockPosition, blockFace, 0));
}
/**
* Finishes the digging process of the fake player.
*
* @param blockPosition The position of the block to be excavated.
* @param blockFace From where the block is struck.
*/
public void finishDigging(Point blockPosition, BlockFace blockFace) {
addToQueue(new ClientPlayerDiggingPacket(ClientPlayerDiggingPacket.Status.FINISHED_DIGGING, blockPosition, blockFace, 0));
}
/**
* Makes the player receives a packet
* WARNING: pretty much unsafe, used internally to redirect packets here,
* you should instead use {@link PlayerConnection#sendPacket(SendablePacket)}
*
* @param serverPacket the packet to consume
*/
public void consumePacket(ServerPacket serverPacket) {
if (serverPacket instanceof PlayerPositionAndLookPacket playerPositionAndLookPacket) {
addToQueue(new ClientTeleportConfirmPacket(playerPositionAndLookPacket.teleportId()));
} else if (serverPacket instanceof KeepAlivePacket keepAlivePacket) {
addToQueue(new ClientKeepAlivePacket(keepAlivePacket.id()));
}
}
/**
* All packets in the queue are executed in the {@link Player#update(long)} method. It is used internally to add all
* received packet from the client. Could be used to "simulate" a received packet, but to use at your own risk!
*
* @param clientPlayPacket The packet to add in the queue.
*/
private void addToQueue(ClientPacket clientPlayPacket) {
this.fakePlayer.addPacketToQueue(clientPlayPacket);
}
}