diff --git a/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch b/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch index df5d35bd..f9af21aa 100644 --- a/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch +++ b/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch @@ -50,1242 +50,3 @@ index d7ebfcfc6a41b7e94a041f7faf3c2d8090fab3c4..cc7c894572652b86b0069325e28f8e73 + @NotNull BotManager getBotManager(); + // Leaves end - Bot API } -diff --git a/src/main/java/org/leavesmc/leaves/entity/Bot.java b/src/main/java/org/leavesmc/leaves/entity/Bot.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7a1ee45d571687317883b896f3ec0a837a8ef450 ---- /dev/null -+++ b/src/main/java/org/leavesmc/leaves/entity/Bot.java -@@ -0,0 +1,80 @@ -+package org.leavesmc.leaves.entity; -+ -+import org.bukkit.entity.Player; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+import org.leavesmc.leaves.entity.botaction.LeavesBotAction; -+ -+import java.util.UUID; -+ -+/** -+ * Represents a fakeplayer -+ */ -+public interface Bot extends Player { -+ -+ /** -+ * Gets the fakeplayer skin -+ * -+ * @return fakeplayer skin name -+ */ -+ @Nullable -+ public String getSkinName(); -+ -+ /** -+ * Gets the fakeplayer name without prefix and suffix -+ * -+ * @return fakeplayer real name -+ */ -+ @NotNull -+ public String getRealName(); -+ -+ /** -+ * Gets the creator's UUID of the fakeplayer -+ * -+ * @return creator's UUID -+ */ -+ @Nullable -+ public UUID getCreatePlayerUUID(); -+ -+ /** -+ * Add an action to the fakeplayer -+ * -+ * @param action bot action -+ */ -+ public void addAction(@NotNull LeavesBotAction action); -+ -+ /** -+ * Get the copy action in giving index -+ * -+ * @param index index of actions -+ * @return Action of that index -+ */ -+ public LeavesBotAction getAction(int index); -+ -+ /** -+ * Get action size -+ * -+ * @return size -+ */ -+ public int getActionSize(); -+ -+ /** -+ * Stop the action in giving index -+ * -+ * @param index index of actions -+ */ -+ public void stopAction(int index); -+ -+ /** -+ * Stop all the actions of the fakeplayer -+ */ -+ public void stopAllActions(); -+ -+ /** -+ * Remove the fakeplayer -+ * -+ * @param save should save -+ * @return success -+ */ -+ public boolean remove(boolean save); -+} -diff --git a/src/main/java/org/leavesmc/leaves/entity/BotCreator.java b/src/main/java/org/leavesmc/leaves/entity/BotCreator.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3c0d985c3e293144f4fef0e447f9bd981bf7912f ---- /dev/null -+++ b/src/main/java/org/leavesmc/leaves/entity/BotCreator.java -@@ -0,0 +1,53 @@ -+package org.leavesmc.leaves.entity; -+ -+import org.bukkit.Bukkit; -+import org.bukkit.Location; -+import org.bukkit.command.CommandSender; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.function.Consumer; -+ -+public interface BotCreator { -+ -+ default BotCreator of(String realName, Location location) { -+ return Bukkit.getBotManager().botCreator(realName, location); -+ } -+ -+ public BotCreator name(String name); -+ -+ public BotCreator skinName(String skinName); -+ -+ public BotCreator skin(String[] skin); -+ -+ /** -+ * Sets the skin of the bot using the Mojang API based on the provided skin name. -+ *
-+ * Need Async. -+ * -+ * @return BotCreator -+ */ -+ public BotCreator mojangAPISkin(); -+ -+ public BotCreator location(@NotNull Location location); -+ -+ public BotCreator creator(@Nullable CommandSender creator); -+ -+ /** -+ * Create a bot directly -+ * -+ * @return a bot, null spawn fail -+ */ -+ @Nullable -+ public Bot spawn(); -+ -+ /** -+ * Create a bot and apply skin of player names `skinName` from MojangAPI -+ * just like `mojangAPISkin().spawn()`, but async -+ *
-+ * you can not get the bot instance instantly because get skin in on async thread
-+ *
-+ * @param consumer Consumer
-+ */
-+ public void spawnWithSkin(Consumer
+ * Need Async.
+ *
+ * @return BotCreator
+ */
+ public BotCreator mojangAPISkin();
+
+ public BotCreator location(@NotNull Location location);
+
+ public BotCreator creator(@Nullable CommandSender creator);
+
+ /**
+ * Create a bot directly
+ *
+ * @return a bot, null spawn fail
+ */
+ @Nullable
+ public Bot spawn();
+
+ /**
+ * Create a bot and apply skin of player names `skinName` from MojangAPI
+ * just like `mojangAPISkin().spawn()`, but async
+ *
+ * you can not get the bot instance instantly because get skin in on async thread
+ *
+ * @param consumer Consumer
+ */
+ public void spawnWithSkin(Consumer> getTabComplete();
-+
-+ /**
-+ * Return a ticks to wait between {@link CustomBotAction#doTick(Bot)}
-+ *
-+ * @return the ticks to wait between runs
-+ */
-+ public int getTickDelay();
-+
-+ /**
-+ * Return a number of times {@link CustomBotAction#doTick(Bot)} can return true
-+ *
-+ * @return the number of times an action can be executed
-+ */
-+ public int getNumber();
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java b/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..8a73f5cc673a95c434677ad7578abfb5402118e3
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java
-@@ -0,0 +1,73 @@
-+package org.leavesmc.leaves.entity.botaction;
-+
-+import org.bukkit.entity.Player;
-+import org.jetbrains.annotations.Nullable;
-+
-+import java.util.UUID;
-+
-+public class LeavesBotAction {
-+
-+ private final String actionName;
-+ private int tickToExecute;
-+ private int executeInterval;
-+ private int remainingExecuteTime;
-+ private final UUID uuid;
-+ private Player actionPlayer;
-+
-+ public LeavesBotAction(BotActionType type, int executeInterval, int remainingExecuteTime) {
-+ this(type.getName(), executeInterval, remainingExecuteTime, UUID.randomUUID());
-+ }
-+
-+ public LeavesBotAction(String name, int executeInterval, int remainingExecuteTime) {
-+ this(name, executeInterval, remainingExecuteTime, UUID.randomUUID());
-+ }
-+
-+ protected LeavesBotAction(String name, int executeInterval, int remainingExecuteTime, UUID actionUUID) {
-+ this.actionName = name;
-+ this.remainingExecuteTime = remainingExecuteTime;
-+ this.executeInterval = executeInterval;
-+ this.uuid = actionUUID;
-+ this.tickToExecute = executeInterval;
-+ }
-+
-+ public void setTickToExecute(int tickToExecute) {
-+ this.tickToExecute = tickToExecute;
-+ }
-+
-+ public int getTickToExecute() {
-+ return tickToExecute;
-+ }
-+
-+ public void setExecuteInterval(int executeInterval) {
-+ this.executeInterval = executeInterval;
-+ }
-+
-+ public int getExecuteInterval() {
-+ return executeInterval;
-+ }
-+
-+ public void setRemainingExecuteTime(int remainingExecuteTime) {
-+ this.remainingExecuteTime = remainingExecuteTime;
-+ }
-+
-+ public int getRemainingExecuteTime() {
-+ return remainingExecuteTime;
-+ }
-+
-+ public String getActionName() {
-+ return actionName;
-+ }
-+
-+ public void setActionPlayer(@Nullable Player actionPlayer) {
-+ this.actionPlayer = actionPlayer;
-+ }
-+
-+ @Nullable
-+ public Player getActionPlayer() {
-+ return actionPlayer;
-+ }
-+
-+ public UUID getUuid() {
-+ return uuid;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..1818aa77f8b051a00b4dbc3da0907cf3462ffcbb
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java
-@@ -0,0 +1,27 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.jetbrains.annotations.NotNull;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+import java.util.UUID;
-+
-+public abstract class BotActionEvent extends BotEvent {
-+
-+ private final String actionName;
-+ private final UUID actionUUID;
-+
-+ public BotActionEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
-+ super(who);
-+ this.actionName = actionName;
-+ this.actionUUID = actionUUID;
-+ }
-+
-+ @NotNull
-+ public String getActionName() {
-+ return actionName;
-+ }
-+
-+ public UUID getActionUUID() {
-+ return actionUUID;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..69a99679d407f974ef0e414945d3bcc7a1a710ea
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java
-@@ -0,0 +1,52 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+import java.util.UUID;
-+
-+public class BotActionExecuteEvent extends BotActionEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ public enum Result {
-+ PASS, SOFT_CANCEL, HARD_CANCEL;
-+
-+ }
-+
-+ private Result result = Result.PASS;
-+
-+ public BotActionExecuteEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
-+ super(who, actionName, actionUUID);
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return result != Result.PASS;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.result = cancel ? Result.SOFT_CANCEL : Result.PASS;
-+ }
-+
-+ public void hardCancel() {
-+ this.result = Result.HARD_CANCEL;
-+ }
-+
-+ public Result getResult() {
-+ return this.result;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-+
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f1123d55712cc90a1d0bb79b7832c90abb77ae90
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java
-@@ -0,0 +1,48 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+import java.util.UUID;
-+
-+public class BotActionScheduleEvent extends BotActionEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private boolean cancel = false;
-+ private final CommandSender sender;
-+
-+ public BotActionScheduleEvent(@NotNull Bot who, String actionName, UUID actionUUID, CommandSender sender) {
-+ super(who, actionName, actionUUID);
-+ this.sender = sender;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Nullable
-+ public CommandSender getSender() {
-+ return sender;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..56822a944a13394f635a7640d76c66b4cc98698a
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java
-@@ -0,0 +1,56 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+import java.util.UUID;
-+
-+public class BotActionStopEvent extends BotActionEvent implements Cancellable{
-+ private static final HandlerList handlers = new HandlerList();
-+ private boolean cancel = false;
-+ private final CommandSender sender;
-+
-+ public enum Reason {
-+ DONE, COMMAND, PLUGIN, INTERNAL
-+ }
-+
-+ private final Reason reason;
-+
-+ public BotActionStopEvent(@NotNull Bot who, String actionName, UUID actionUUID, Reason stopReason, CommandSender sender) {
-+ super(who, actionName, actionUUID);
-+ this.reason = stopReason;
-+ this.sender = sender;
-+ }
-+
-+ public Reason getReason() {
-+ return reason;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Nullable
-+ public CommandSender getSender() {
-+ return sender;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..5afa2df3566dd038b9f262ce8bc1f01c688b45a5
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java
-@@ -0,0 +1,53 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+public class BotConfigModifyEvent extends BotEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private final String configName;
-+ private final String[] configValue;
-+ private boolean cancel;
-+ private final CommandSender sender;
-+
-+ public BotConfigModifyEvent(@NotNull Bot who, String configName, String[] configValue, CommandSender sender) {
-+ super(who);
-+ this.configName = configName;
-+ this.configValue = configValue;
-+ this.sender = sender;
-+ }
-+
-+ @NotNull
-+ public String getConfigName() {
-+ return configName;
-+ }
-+
-+ @NotNull
-+ public String[] getConfigValue() {
-+ return configValue;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..11e55a99b91e4ea11640d3fe02bc7f0106d7b91c
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java
-@@ -0,0 +1,119 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.Location;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.Event;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * Call when a fakeplayer created
-+ */
-+public class BotCreateEvent extends Event implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ public enum CreateReason {
-+ COMMAND,
-+ PLUGIN,
-+ INTERNAL,
-+ UNKNOWN,
-+ }
-+
-+ private final String bot;
-+ private final String skin;
-+ private final CreateReason reason;
-+ private final CommandSender creator;
-+ private Location createLocation;
-+ private boolean cancel = false;
-+
-+ public BotCreateEvent(@NotNull final String who, @NotNull final String skin, @NotNull final Location createLocation, @NotNull CreateReason reason, @Nullable CommandSender creator) {
-+ this.bot = who;
-+ this.skin = skin;
-+ this.createLocation = createLocation;
-+ this.reason = reason;
-+ this.creator = creator;
-+ }
-+
-+ /**
-+ * Gets the fakeplayer name
-+ *
-+ * @return fakeplayer name
-+ */
-+ public String getBot() {
-+ return bot;
-+ }
-+
-+ /**
-+ * Gets the location to create the fakeplayer
-+ *
-+ * @return Location to create the fakeplayer
-+ */
-+ @NotNull
-+ public Location getCreateLocation() {
-+ return createLocation;
-+ }
-+
-+ /**
-+ * Sets the location to create the fakeplayer
-+ *
-+ * @param createLocation location to create the fakeplayer
-+ */
-+ public void setCreateLocation(@NotNull Location createLocation) {
-+ this.createLocation = createLocation;
-+ }
-+
-+ /**
-+ * Gets the fakeplayer skin
-+ *
-+ * @return fakeplayer skin name
-+ */
-+ @Nullable
-+ public String getSkin() {
-+ return skin;
-+ }
-+
-+ /**
-+ * Gets the create reason of the bot
-+ *
-+ * @return create reason
-+ */
-+ @NotNull
-+ public CreateReason getReason() {
-+ return reason;
-+ }
-+
-+ /**
-+ * Gets the creator of the bot
-+ * if the create reason is not COMMAND, the creator might be null
-+ *
-+ * @return An optional of creator
-+ */
-+ @Nullable
-+ public CommandSender getCreator() {
-+ return creator;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..3366b50cf1835129a027b5342e4d8cf070cecf4a
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java
-@@ -0,0 +1,69 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import net.kyori.adventure.text.Component;
-+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+public class BotDeathEvent extends BotEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private boolean cancel = false;
-+ private boolean sendDeathMessage;
-+ private Component deathMessage;
-+
-+ public BotDeathEvent(@NotNull Bot who, @Nullable Component deathMessage, boolean sendDeathMessage) {
-+ super(who);
-+ this.deathMessage = deathMessage;
-+ this.sendDeathMessage = sendDeathMessage;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+
-+ public Component deathMessage() {
-+ return deathMessage;
-+ }
-+
-+ public void deathMessage(Component deathMessage) {
-+ this.deathMessage = deathMessage;
-+ }
-+
-+ @Nullable
-+ public String getDeathMessage() {
-+ return this.deathMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.deathMessage);
-+ }
-+
-+ public void setDeathMessage(@Nullable String deathMessage) {
-+ this.deathMessage = deathMessage != null ? LegacyComponentSerializer.legacySection().deserialize(deathMessage) : null;
-+ }
-+
-+ public boolean isSendDeathMessage() {
-+ return sendDeathMessage;
-+ }
-+
-+ public void setSendDeathMessage(boolean sendDeathMessage) {
-+ this.sendDeathMessage = sendDeathMessage;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..ed9f954da5d381368977eae3ed19a334a3bc3e5a
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java
-@@ -0,0 +1,32 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.event.Event;
-+import org.jetbrains.annotations.NotNull;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+/**
-+ * Represents a fakeplayer related event
-+ */
-+public abstract class BotEvent extends Event {
-+
-+ protected Bot bot;
-+
-+ public BotEvent(@NotNull final Bot who) {
-+ bot = who;
-+ }
-+
-+ public BotEvent(@NotNull final Bot who, boolean async) {
-+ super(async);
-+ bot = who;
-+ }
-+
-+ /**
-+ * Returns the fakeplayer involved in this event
-+ *
-+ * @return Fakeplayer who is involved in this event
-+ */
-+ @NotNull
-+ public final Bot getBot() {
-+ return bot;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..8191c0e1302234981212d2fa015425e25825ce61
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java
-@@ -0,0 +1,46 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+public class BotInventoryOpenEvent extends BotEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private final Player player;
-+ private boolean cancel = false;
-+
-+ public BotInventoryOpenEvent(@NotNull Bot who, @Nullable Player player) {
-+ super(who);
-+ this.player = player;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Nullable
-+ public Player getOpenPlayer() {
-+ return player;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..24e5f4d833897000e0378d4d3c3ff75c08a5bad2
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java
-@@ -0,0 +1,67 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import net.kyori.adventure.text.Component;
-+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+/**
-+ * Called when a fakeplayer joins a server
-+ */
-+public class BotJoinEvent extends BotEvent {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private Component joinMessage;
-+
-+ public BotJoinEvent(@NotNull final Bot who, @Nullable final Component joinMessage) {
-+ super(who);
-+ this.joinMessage = joinMessage;
-+ }
-+
-+ public BotJoinEvent(@NotNull final Bot who, @Nullable final String joinMessage) {
-+ super(who);
-+ this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
-+ }
-+
-+ public void joinMessage(@Nullable final Component joinMessage) {
-+ this.joinMessage = joinMessage;
-+ }
-+
-+ @Nullable
-+ public Component joinMessage() {
-+ return joinMessage;
-+ }
-+
-+ /**
-+ * Gets the join message to send to all online players
-+ *
-+ * @return string join message. Can be null
-+ */
-+ @Nullable
-+ public String getJoinMessage() {
-+ return this.joinMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.joinMessage);
-+ }
-+
-+ /**
-+ * Sets the join message to send to all online players
-+ *
-+ * @param joinMessage join message. If null, no message will be sent
-+ */
-+ public void setJoinMessage(@Nullable String joinMessage) {
-+ this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..b5295bddb627985ff35ca4de55253bbd68593655
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java
-@@ -0,0 +1,59 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.Event;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+
-+import java.util.UUID;
-+
-+/**
-+ * Call when a fakeplayer loaded
-+ */
-+public class BotLoadEvent extends Event implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private final String bot;
-+ private final UUID botUUID;
-+ private boolean cancel = false;
-+
-+ public BotLoadEvent(@NotNull final String who, @NotNull final UUID uuid) {
-+ this.bot = who;
-+ this.botUUID = uuid;
-+ }
-+
-+ /**
-+ * Gets the fakeplayer name
-+ *
-+ * @return fakeplayer name
-+ */
-+ public String getBot() {
-+ return bot;
-+ }
-+
-+ public UUID getBotUUID() {
-+ return botUUID;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..76909d03f2b23602335b91f3b168e17f20256d34
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java
-@@ -0,0 +1,106 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import net.kyori.adventure.text.Component;
-+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+/**
-+ * Call when a fakeplayer removed
-+ */
-+public class BotRemoveEvent extends BotEvent implements Cancellable {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ public enum RemoveReason {
-+ COMMAND,
-+ PLUGIN,
-+ DEATH,
-+ INTERNAL
-+ }
-+
-+ private final RemoveReason reason;
-+ private final CommandSender remover;
-+ private Component removeMessage;
-+ private boolean save;
-+ private boolean cancel = false;
-+
-+ public BotRemoveEvent(@NotNull final Bot who, @NotNull RemoveReason reason, @Nullable CommandSender remover, @Nullable Component removeMessage, boolean save) {
-+ super(who);
-+ this.reason = reason;
-+ this.remover = remover;
-+ this.removeMessage = removeMessage;
-+ this.save = save;
-+ }
-+
-+ /**
-+ * Gets the remove reason of the bot
-+ *
-+ * @return remove reason
-+ */
-+ @NotNull
-+ public RemoveReason getReason() {
-+ return reason;
-+ }
-+
-+ /**
-+ * Gets the remover of the bot
-+ * if the remove reason is not COMMAND, the creator might be null
-+ *
-+ * @return An optional of remover
-+ */
-+ @Nullable
-+ public CommandSender getRemover() {
-+ return remover;
-+ }
-+
-+ public Component removeMessage() {
-+ return removeMessage;
-+ }
-+
-+ public void removeMessage(Component removeMessage) {
-+ this.removeMessage = removeMessage;
-+ }
-+
-+ @Nullable
-+ public String getRemoveMessage() {
-+ return this.removeMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.removeMessage);
-+ }
-+
-+ public void setRemoveMessage(@Nullable String removeMessage) {
-+ this.removeMessage = removeMessage != null ? LegacyComponentSerializer.legacySection().deserialize(removeMessage) : null;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ public boolean shouldSave() {
-+ return save;
-+ }
-+
-+ public void setSave(boolean save) {
-+ this.save = save;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java b/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..46ab3e9b5e398ec238e129d16fb020b481a88f76
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java
-@@ -0,0 +1,38 @@
-+package org.leavesmc.leaves.event.bot;
-+
-+import org.bukkit.Location;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.leavesmc.leaves.entity.Bot;
-+
-+public class BotSpawnLocationEvent extends BotEvent {
-+
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private Location spawnLocation;
-+
-+ public BotSpawnLocationEvent(@NotNull final Bot who, @NotNull Location spawnLocation) {
-+ super(who);
-+ this.spawnLocation = spawnLocation;
-+ }
-+
-+ @NotNull
-+ public Location getSpawnLocation() {
-+ return spawnLocation;
-+ }
-+
-+ public void setSpawnLocation(@NotNull Location location) {
-+ this.spawnLocation = location;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/leaves-api/paper-patches/features/0005-Force-peaceful-mode-switch.patch b/leaves-api/paper-patches/features/0004-Force-peaceful-mode-switch.patch
similarity index 100%
rename from leaves-api/paper-patches/features/0005-Force-peaceful-mode-switch.patch
rename to leaves-api/paper-patches/features/0004-Force-peaceful-mode-switch.patch
diff --git a/leaves-api/paper-patches/features/0004-Player-operation-limiter.patch b/leaves-api/paper-patches/features/0004-Player-operation-limiter.patch
deleted file mode 100644
index d0a9c98f..00000000
--- a/leaves-api/paper-patches/features/0004-Player-operation-limiter.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: violetc <58360096+s-yh-china@users.noreply.github.com>
-Date: Sun, 11 Dec 2022 18:43:36 +0800
-Subject: [PATCH] Player operation limiter
-
-
-diff --git a/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java b/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..1f5852eb6d53db5774db0ab4eba50bcb8733cfd6
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java
-@@ -0,0 +1,56 @@
-+package org.leavesmc.leaves.event.player;
-+
-+import org.bukkit.block.Block;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.player.PlayerEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a player operation is limited
-+ */
-+public class PlayerOperationLimitEvent extends PlayerEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ private final Block block;
-+ private final Operation operation;
-+
-+ public PlayerOperationLimitEvent(@NotNull Player who, Operation operation, Block block) {
-+ super(who);
-+ this.block = block;
-+ this.operation = operation;
-+ }
-+
-+ /**
-+ * Gets the operated block
-+ *
-+ * @return block
-+ */
-+ public Block getBlock() {
-+ return block;
-+ }
-+
-+ /**
-+ * Gets the type of operation
-+ *
-+ * @return operation type
-+ */
-+ public Operation getOperation() {
-+ return operation;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+
-+ public enum Operation {
-+ MINE, PLACE
-+ }
-+}
diff --git a/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch b/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch
new file mode 100644
index 00000000..d7a3080a
--- /dev/null
+++ b/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
+Date: Sun, 26 Jan 2025 01:39:16 -0500
+Subject: [PATCH] Replay Mod API
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index a5b316601b433544b604455dc1c8079bf478b43e..b73c009ece3af3daf8251adb7502ee9c8ad103c4 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -3006,4 +3006,10 @@ public final class Bukkit {
+ return server.getBotManager();
+ }
+ // Leaves end - Bot API
++
++ // Leaves start - Photographer API
++ public static @NotNull org.leavesmc.leaves.entity.PhotographerManager getPhotographerManager() {
++ return server.getPhotographerManager();
++ }
++ // Leaves end - Photographer API
+ }
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index cc7c894572652b86b0069325e28f8e73f7a66f01..632d510f4ae42c5bbb00320b517659c857ccded7 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -67,6 +67,7 @@ import org.jetbrains.annotations.Contract;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+ import org.leavesmc.leaves.entity.BotManager;
++import org.leavesmc.leaves.entity.PhotographerManager;
+
+ /**
+ * Represents a server implementation.
+@@ -2708,4 +2709,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ */
+ @NotNull BotManager getBotManager();
+ // Leaves end - Bot API
++
++ // Leaves start - Photographer API
++ @NotNull PhotographerManager getPhotographerManager();
++ // Leaves end - Photographer API
+ }
diff --git a/leaves-api/paper-patches/features/0006-Bytebuf-API.patch b/leaves-api/paper-patches/features/0006-Bytebuf-API.patch
new file mode 100644
index 00000000..7800a561
--- /dev/null
+++ b/leaves-api/paper-patches/features/0006-Bytebuf-API.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
+Date: Wed, 22 May 2024 10:12:04 +0800
+Subject: [PATCH] Bytebuf API
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index b73c009ece3af3daf8251adb7502ee9c8ad103c4..dfc3da5eef1b8c11c5deef3853e47a7f685f0d46 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -3012,4 +3012,10 @@ public final class Bukkit {
+ return server.getPhotographerManager();
+ }
+ // Leaves end - Photographer API
++
++ // Leaves start - Bytebuf API
++ public static org.leavesmc.leaves.bytebuf.BytebufManager getBytebufManager() {
++ return server.getBytebufManager();
++ }
++ // Leaves end - Bytebuf API
+ }
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 632d510f4ae42c5bbb00320b517659c857ccded7..ef91d117defb23b27a8ca01551753716dc24d184 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -2713,4 +2713,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ // Leaves start - Photographer API
+ @NotNull PhotographerManager getPhotographerManager();
+ // Leaves end - Photographer API
++
++ // Leaves start - Bytebuf API
++ org.leavesmc.leaves.bytebuf.BytebufManager getBytebufManager();
++ // Leaves end - Bytebuf API
+ }
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 69f982d1dbffa256b65c32292805320452a9842f..9041dbf1aaa0a8f548b3122817cf6ef3b9c56ba2 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -3853,6 +3853,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ boolean isChunkSent(long chunkKey);
+ // Paper end
+
++ // Leaves start - Bytebuf API
++ void sendPacket(org.leavesmc.leaves.bytebuf.packet.Packet packet);
++
++ void sendPacket(org.leavesmc.leaves.bytebuf.Bytebuf buf, org.leavesmc.leaves.bytebuf.packet.PacketType type);
++ // Leaves end - Bytebuf API
++
+ @Override
+ Spigot spigot();
+ // Spigot end
diff --git a/leaves-api/paper-patches/features/0006-Replay-Mod-API.patch b/leaves-api/paper-patches/features/0006-Replay-Mod-API.patch
deleted file mode 100644
index 194ed8a2..00000000
--- a/leaves-api/paper-patches/features/0006-Replay-Mod-API.patch
+++ /dev/null
@@ -1,138 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
-Date: Sun, 26 Jan 2025 01:39:16 -0500
-Subject: [PATCH] Replay Mod API
-
-
-diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
-index a5b316601b433544b604455dc1c8079bf478b43e..b73c009ece3af3daf8251adb7502ee9c8ad103c4 100644
---- a/src/main/java/org/bukkit/Bukkit.java
-+++ b/src/main/java/org/bukkit/Bukkit.java
-@@ -3006,4 +3006,10 @@ public final class Bukkit {
- return server.getBotManager();
- }
- // Leaves end - Bot API
-+
-+ // Leaves start - Photographer API
-+ public static @NotNull org.leavesmc.leaves.entity.PhotographerManager getPhotographerManager() {
-+ return server.getPhotographerManager();
-+ }
-+ // Leaves end - Photographer API
- }
-diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
-index cc7c894572652b86b0069325e28f8e73f7a66f01..632d510f4ae42c5bbb00320b517659c857ccded7 100644
---- a/src/main/java/org/bukkit/Server.java
-+++ b/src/main/java/org/bukkit/Server.java
-@@ -67,6 +67,7 @@ import org.jetbrains.annotations.Contract;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
- import org.leavesmc.leaves.entity.BotManager;
-+import org.leavesmc.leaves.entity.PhotographerManager;
-
- /**
- * Represents a server implementation.
-@@ -2708,4 +2709,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
- */
- @NotNull BotManager getBotManager();
- // Leaves end - Bot API
-+
-+ // Leaves start - Photographer API
-+ @NotNull PhotographerManager getPhotographerManager();
-+ // Leaves end - Photographer API
- }
-diff --git a/src/main/java/org/leavesmc/leaves/entity/Photographer.java b/src/main/java/org/leavesmc/leaves/entity/Photographer.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..cc4226c59aa9f5942bd90e270c5bcd8b354139dd
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/entity/Photographer.java
-@@ -0,0 +1,27 @@
-+package org.leavesmc.leaves.entity;
-+
-+import org.bukkit.entity.Player;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+import java.io.File;
-+
-+public interface Photographer extends Player {
-+
-+ @NotNull
-+ public String getId();
-+
-+ public void setRecordFile(@NotNull File file);
-+
-+ public void stopRecording();
-+
-+ public void stopRecording(boolean async);
-+
-+ public void stopRecording(boolean async, boolean save);
-+
-+ public void pauseRecording();
-+
-+ public void resumeRecording();
-+
-+ public void setFollowPlayer(@Nullable Player player);
-+}
-diff --git a/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java b/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..492414e9328b3a0cde2157068f00e60eb5e978c6
---- /dev/null
-+++ b/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java
-@@ -0,0 +1,33 @@
-+package org.leavesmc.leaves.entity;
-+
-+import org.bukkit.Location;
-+import org.bukkit.util.Consumer;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.leavesmc.leaves.entity.botaction.CustomBotAction;
-+import org.leavesmc.leaves.replay.BukkitRecorderOption;
-+
-+import java.util.Collection;
-+import java.util.UUID;
-+
-+public interface PhotographerManager {
-+ @Nullable
-+ public Photographer getPhotographer(@NotNull UUID uuid);
-+
-+ @Nullable
-+ public Photographer getPhotographer(@NotNull String id);
-+
-+ @Nullable
-+ public Photographer createPhotographer(@NotNull String id, @NotNull Location location);
-+
-+ @Nullable
-+ public Photographer createPhotographer(@NotNull String id, @NotNull Location location, @NotNull BukkitRecorderOption recorderOption);
-+
-+ public void removePhotographer(@NotNull String id);
-+
-+ public void removePhotographer(@NotNull UUID uuid);
-+
-+ public void removeAllPhotographers();
-+
-+ public Collection
> getTabComplete();
+
+ /**
+ * Return a ticks to wait between {@link CustomBotAction#doTick(Bot)}
+ *
+ * @return the ticks to wait between runs
+ */
+ public int getTickDelay();
+
+ /**
+ * Return a number of times {@link CustomBotAction#doTick(Bot)} can return true
+ *
+ * @return the number of times an action can be executed
+ */
+ public int getNumber();
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java b/leaves-api/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java
new file mode 100644
index 00000000..8a73f5cc
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java
@@ -0,0 +1,73 @@
+package org.leavesmc.leaves.entity.botaction;
+
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+public class LeavesBotAction {
+
+ private final String actionName;
+ private int tickToExecute;
+ private int executeInterval;
+ private int remainingExecuteTime;
+ private final UUID uuid;
+ private Player actionPlayer;
+
+ public LeavesBotAction(BotActionType type, int executeInterval, int remainingExecuteTime) {
+ this(type.getName(), executeInterval, remainingExecuteTime, UUID.randomUUID());
+ }
+
+ public LeavesBotAction(String name, int executeInterval, int remainingExecuteTime) {
+ this(name, executeInterval, remainingExecuteTime, UUID.randomUUID());
+ }
+
+ protected LeavesBotAction(String name, int executeInterval, int remainingExecuteTime, UUID actionUUID) {
+ this.actionName = name;
+ this.remainingExecuteTime = remainingExecuteTime;
+ this.executeInterval = executeInterval;
+ this.uuid = actionUUID;
+ this.tickToExecute = executeInterval;
+ }
+
+ public void setTickToExecute(int tickToExecute) {
+ this.tickToExecute = tickToExecute;
+ }
+
+ public int getTickToExecute() {
+ return tickToExecute;
+ }
+
+ public void setExecuteInterval(int executeInterval) {
+ this.executeInterval = executeInterval;
+ }
+
+ public int getExecuteInterval() {
+ return executeInterval;
+ }
+
+ public void setRemainingExecuteTime(int remainingExecuteTime) {
+ this.remainingExecuteTime = remainingExecuteTime;
+ }
+
+ public int getRemainingExecuteTime() {
+ return remainingExecuteTime;
+ }
+
+ public String getActionName() {
+ return actionName;
+ }
+
+ public void setActionPlayer(@Nullable Player actionPlayer) {
+ this.actionPlayer = actionPlayer;
+ }
+
+ @Nullable
+ public Player getActionPlayer() {
+ return actionPlayer;
+ }
+
+ public UUID getUuid() {
+ return uuid;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java
new file mode 100644
index 00000000..1818aa77
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java
@@ -0,0 +1,27 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.jetbrains.annotations.NotNull;
+import org.leavesmc.leaves.entity.Bot;
+
+import java.util.UUID;
+
+public abstract class BotActionEvent extends BotEvent {
+
+ private final String actionName;
+ private final UUID actionUUID;
+
+ public BotActionEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
+ super(who);
+ this.actionName = actionName;
+ this.actionUUID = actionUUID;
+ }
+
+ @NotNull
+ public String getActionName() {
+ return actionName;
+ }
+
+ public UUID getActionUUID() {
+ return actionUUID;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java
new file mode 100644
index 00000000..69a99679
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionExecuteEvent.java
@@ -0,0 +1,52 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.leavesmc.leaves.entity.Bot;
+
+import java.util.UUID;
+
+public class BotActionExecuteEvent extends BotActionEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ public enum Result {
+ PASS, SOFT_CANCEL, HARD_CANCEL;
+
+ }
+
+ private Result result = Result.PASS;
+
+ public BotActionExecuteEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
+ super(who, actionName, actionUUID);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return result != Result.PASS;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.result = cancel ? Result.SOFT_CANCEL : Result.PASS;
+ }
+
+ public void hardCancel() {
+ this.result = Result.HARD_CANCEL;
+ }
+
+ public Result getResult() {
+ return this.result;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
+
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java
new file mode 100644
index 00000000..f1123d55
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java
@@ -0,0 +1,48 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+import java.util.UUID;
+
+public class BotActionScheduleEvent extends BotActionEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private boolean cancel = false;
+ private final CommandSender sender;
+
+ public BotActionScheduleEvent(@NotNull Bot who, String actionName, UUID actionUUID, CommandSender sender) {
+ super(who, actionName, actionUUID);
+ this.sender = sender;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Nullable
+ public CommandSender getSender() {
+ return sender;
+ }
+
+ @Override
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java
new file mode 100644
index 00000000..56822a94
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java
@@ -0,0 +1,56 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+import java.util.UUID;
+
+public class BotActionStopEvent extends BotActionEvent implements Cancellable{
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancel = false;
+ private final CommandSender sender;
+
+ public enum Reason {
+ DONE, COMMAND, PLUGIN, INTERNAL
+ }
+
+ private final Reason reason;
+
+ public BotActionStopEvent(@NotNull Bot who, String actionName, UUID actionUUID, Reason stopReason, CommandSender sender) {
+ super(who, actionName, actionUUID);
+ this.reason = stopReason;
+ this.sender = sender;
+ }
+
+ public Reason getReason() {
+ return reason;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Nullable
+ public CommandSender getSender() {
+ return sender;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java
new file mode 100644
index 00000000..5afa2df3
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java
@@ -0,0 +1,53 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.leavesmc.leaves.entity.Bot;
+
+public class BotConfigModifyEvent extends BotEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private final String configName;
+ private final String[] configValue;
+ private boolean cancel;
+ private final CommandSender sender;
+
+ public BotConfigModifyEvent(@NotNull Bot who, String configName, String[] configValue, CommandSender sender) {
+ super(who);
+ this.configName = configName;
+ this.configValue = configValue;
+ this.sender = sender;
+ }
+
+ @NotNull
+ public String getConfigName() {
+ return configName;
+ }
+
+ @NotNull
+ public String[] getConfigValue() {
+ return configValue;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java
new file mode 100644
index 00000000..11e55a99
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java
@@ -0,0 +1,119 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Call when a fakeplayer created
+ */
+public class BotCreateEvent extends Event implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ public enum CreateReason {
+ COMMAND,
+ PLUGIN,
+ INTERNAL,
+ UNKNOWN,
+ }
+
+ private final String bot;
+ private final String skin;
+ private final CreateReason reason;
+ private final CommandSender creator;
+ private Location createLocation;
+ private boolean cancel = false;
+
+ public BotCreateEvent(@NotNull final String who, @NotNull final String skin, @NotNull final Location createLocation, @NotNull CreateReason reason, @Nullable CommandSender creator) {
+ this.bot = who;
+ this.skin = skin;
+ this.createLocation = createLocation;
+ this.reason = reason;
+ this.creator = creator;
+ }
+
+ /**
+ * Gets the fakeplayer name
+ *
+ * @return fakeplayer name
+ */
+ public String getBot() {
+ return bot;
+ }
+
+ /**
+ * Gets the location to create the fakeplayer
+ *
+ * @return Location to create the fakeplayer
+ */
+ @NotNull
+ public Location getCreateLocation() {
+ return createLocation;
+ }
+
+ /**
+ * Sets the location to create the fakeplayer
+ *
+ * @param createLocation location to create the fakeplayer
+ */
+ public void setCreateLocation(@NotNull Location createLocation) {
+ this.createLocation = createLocation;
+ }
+
+ /**
+ * Gets the fakeplayer skin
+ *
+ * @return fakeplayer skin name
+ */
+ @Nullable
+ public String getSkin() {
+ return skin;
+ }
+
+ /**
+ * Gets the create reason of the bot
+ *
+ * @return create reason
+ */
+ @NotNull
+ public CreateReason getReason() {
+ return reason;
+ }
+
+ /**
+ * Gets the creator of the bot
+ * if the create reason is not COMMAND, the creator might be null
+ *
+ * @return An optional of creator
+ */
+ @Nullable
+ public CommandSender getCreator() {
+ return creator;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Override
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java
new file mode 100644
index 00000000..3366b50c
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotDeathEvent.java
@@ -0,0 +1,69 @@
+package org.leavesmc.leaves.event.bot;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+public class BotDeathEvent extends BotEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private boolean cancel = false;
+ private boolean sendDeathMessage;
+ private Component deathMessage;
+
+ public BotDeathEvent(@NotNull Bot who, @Nullable Component deathMessage, boolean sendDeathMessage) {
+ super(who);
+ this.deathMessage = deathMessage;
+ this.sendDeathMessage = sendDeathMessage;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ public Component deathMessage() {
+ return deathMessage;
+ }
+
+ public void deathMessage(Component deathMessage) {
+ this.deathMessage = deathMessage;
+ }
+
+ @Nullable
+ public String getDeathMessage() {
+ return this.deathMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.deathMessage);
+ }
+
+ public void setDeathMessage(@Nullable String deathMessage) {
+ this.deathMessage = deathMessage != null ? LegacyComponentSerializer.legacySection().deserialize(deathMessage) : null;
+ }
+
+ public boolean isSendDeathMessage() {
+ return sendDeathMessage;
+ }
+
+ public void setSendDeathMessage(boolean sendDeathMessage) {
+ this.sendDeathMessage = sendDeathMessage;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java
new file mode 100644
index 00000000..ed9f954d
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java
@@ -0,0 +1,32 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.NotNull;
+import org.leavesmc.leaves.entity.Bot;
+
+/**
+ * Represents a fakeplayer related event
+ */
+public abstract class BotEvent extends Event {
+
+ protected Bot bot;
+
+ public BotEvent(@NotNull final Bot who) {
+ bot = who;
+ }
+
+ public BotEvent(@NotNull final Bot who, boolean async) {
+ super(async);
+ bot = who;
+ }
+
+ /**
+ * Returns the fakeplayer involved in this event
+ *
+ * @return Fakeplayer who is involved in this event
+ */
+ @NotNull
+ public final Bot getBot() {
+ return bot;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java
new file mode 100644
index 00000000..8191c0e1
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java
@@ -0,0 +1,46 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+public class BotInventoryOpenEvent extends BotEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private final Player player;
+ private boolean cancel = false;
+
+ public BotInventoryOpenEvent(@NotNull Bot who, @Nullable Player player) {
+ super(who);
+ this.player = player;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Nullable
+ public Player getOpenPlayer() {
+ return player;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java
new file mode 100644
index 00000000..24e5f4d8
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java
@@ -0,0 +1,67 @@
+package org.leavesmc.leaves.event.bot;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+/**
+ * Called when a fakeplayer joins a server
+ */
+public class BotJoinEvent extends BotEvent {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private Component joinMessage;
+
+ public BotJoinEvent(@NotNull final Bot who, @Nullable final Component joinMessage) {
+ super(who);
+ this.joinMessage = joinMessage;
+ }
+
+ public BotJoinEvent(@NotNull final Bot who, @Nullable final String joinMessage) {
+ super(who);
+ this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
+ }
+
+ public void joinMessage(@Nullable final Component joinMessage) {
+ this.joinMessage = joinMessage;
+ }
+
+ @Nullable
+ public Component joinMessage() {
+ return joinMessage;
+ }
+
+ /**
+ * Gets the join message to send to all online players
+ *
+ * @return string join message. Can be null
+ */
+ @Nullable
+ public String getJoinMessage() {
+ return this.joinMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.joinMessage);
+ }
+
+ /**
+ * Sets the join message to send to all online players
+ *
+ * @param joinMessage join message. If null, no message will be sent
+ */
+ public void setJoinMessage(@Nullable String joinMessage) {
+ this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
+ }
+
+ @Override
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java
new file mode 100644
index 00000000..b5295bdd
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java
@@ -0,0 +1,59 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+/**
+ * Call when a fakeplayer loaded
+ */
+public class BotLoadEvent extends Event implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private final String bot;
+ private final UUID botUUID;
+ private boolean cancel = false;
+
+ public BotLoadEvent(@NotNull final String who, @NotNull final UUID uuid) {
+ this.bot = who;
+ this.botUUID = uuid;
+ }
+
+ /**
+ * Gets the fakeplayer name
+ *
+ * @return fakeplayer name
+ */
+ public String getBot() {
+ return bot;
+ }
+
+ public UUID getBotUUID() {
+ return botUUID;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ @Override
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java
new file mode 100644
index 00000000..76909d03
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java
@@ -0,0 +1,106 @@
+package org.leavesmc.leaves.event.bot;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.leavesmc.leaves.entity.Bot;
+
+/**
+ * Call when a fakeplayer removed
+ */
+public class BotRemoveEvent extends BotEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ public enum RemoveReason {
+ COMMAND,
+ PLUGIN,
+ DEATH,
+ INTERNAL
+ }
+
+ private final RemoveReason reason;
+ private final CommandSender remover;
+ private Component removeMessage;
+ private boolean save;
+ private boolean cancel = false;
+
+ public BotRemoveEvent(@NotNull final Bot who, @NotNull RemoveReason reason, @Nullable CommandSender remover, @Nullable Component removeMessage, boolean save) {
+ super(who);
+ this.reason = reason;
+ this.remover = remover;
+ this.removeMessage = removeMessage;
+ this.save = save;
+ }
+
+ /**
+ * Gets the remove reason of the bot
+ *
+ * @return remove reason
+ */
+ @NotNull
+ public RemoveReason getReason() {
+ return reason;
+ }
+
+ /**
+ * Gets the remover of the bot
+ * if the remove reason is not COMMAND, the creator might be null
+ *
+ * @return An optional of remover
+ */
+ @Nullable
+ public CommandSender getRemover() {
+ return remover;
+ }
+
+ public Component removeMessage() {
+ return removeMessage;
+ }
+
+ public void removeMessage(Component removeMessage) {
+ this.removeMessage = removeMessage;
+ }
+
+ @Nullable
+ public String getRemoveMessage() {
+ return this.removeMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.removeMessage);
+ }
+
+ public void setRemoveMessage(@Nullable String removeMessage) {
+ this.removeMessage = removeMessage != null ? LegacyComponentSerializer.legacySection().deserialize(removeMessage) : null;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
+ }
+
+ public boolean shouldSave() {
+ return save;
+ }
+
+ public void setSave(boolean save) {
+ this.save = save;
+ }
+
+ @Override
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java
new file mode 100644
index 00000000..46ab3e9b
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/bot/BotSpawnLocationEvent.java
@@ -0,0 +1,38 @@
+package org.leavesmc.leaves.event.bot;
+
+import org.bukkit.Location;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.leavesmc.leaves.entity.Bot;
+
+public class BotSpawnLocationEvent extends BotEvent {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private Location spawnLocation;
+
+ public BotSpawnLocationEvent(@NotNull final Bot who, @NotNull Location spawnLocation) {
+ super(who);
+ this.spawnLocation = spawnLocation;
+ }
+
+ @NotNull
+ public Location getSpawnLocation() {
+ return spawnLocation;
+ }
+
+ public void setSpawnLocation(@NotNull Location location) {
+ this.spawnLocation = location;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java b/leaves-api/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java
new file mode 100644
index 00000000..1f5852eb
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/event/player/PlayerOperationLimitEvent.java
@@ -0,0 +1,56 @@
+package org.leavesmc.leaves.event.player;
+
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called when a player operation is limited
+ */
+public class PlayerOperationLimitEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+
+ private final Block block;
+ private final Operation operation;
+
+ public PlayerOperationLimitEvent(@NotNull Player who, Operation operation, Block block) {
+ super(who);
+ this.block = block;
+ this.operation = operation;
+ }
+
+ /**
+ * Gets the operated block
+ *
+ * @return block
+ */
+ public Block getBlock() {
+ return block;
+ }
+
+ /**
+ * Gets the type of operation
+ *
+ * @return operation type
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ public enum Operation {
+ MINE, PLACE
+ }
+}
diff --git a/leaves-api/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java b/leaves-api/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java
new file mode 100644
index 00000000..320e8bc0
--- /dev/null
+++ b/leaves-api/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java
@@ -0,0 +1,18 @@
+package org.leavesmc.leaves.replay;
+
+public class BukkitRecorderOption {
+
+ // public int recordDistance = -1;
+ public String serverName = "Leaves";
+ public BukkitRecordWeather forceWeather = BukkitRecordWeather.NULL;
+ public int forceDayTime = -1;
+ public boolean ignoreChat = false;
+ // public boolean ignoreItem = false;
+
+ public enum BukkitRecordWeather {
+ CLEAR,
+ RAIN,
+ THUNDER,
+ NULL
+ }
+}