diff --git a/patches/api/0003-Add-fakeplayer-api.patch b/patches/api/0003-Add-fakeplayer-api.patch index 69c6868b..b32578f9 100644 --- a/patches/api/0003-Add-fakeplayer-api.patch +++ b/patches/api/0003-Add-fakeplayer-api.patch @@ -63,10 +63,10 @@ index 594deedd08c3b3255fe6838471d945759f09a182..6fa638198f75458177af795f00250ce9 } 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..e5cf058a5c7bf14c0c2f017d4b6fd3bd8ed0aa3d +index 0000000000000000000000000000000000000000..7a1ee45d571687317883b896f3ec0a837a8ef450 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/Bot.java -@@ -0,0 +1,35 @@ +@@ -0,0 +1,80 @@ +package org.leavesmc.leaves.entity; + +import org.bukkit.entity.Player; @@ -97,22 +97,70 @@ index 0000000000000000000000000000000000000000..e5cf058a5c7bf14c0c2f017d4b6fd3bd + @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..fd18396f8d127f3f044ee407e4c8a3dc7b2c02b5 +index 0000000000000000000000000000000000000000..b65e4661f7641c2e0d94c47c7688934fe2be5054 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/BotCreator.java -@@ -0,0 +1,20 @@ +@@ -0,0 +1,47 @@ +package org.leavesmc.leaves.entity; + +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 { + @@ -122,18 +170,42 @@ index 0000000000000000000000000000000000000000..fd18396f8d127f3f044ee407e4c8a3dc + + 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(CommandSender creator); ++ 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 ++ *

++ * you can not get the bot instance instantly because get skin in on async thread ++ * ++ * @param consumer Consumer ++ */ ++ public void spawnWithSkin(Consumer consumer); +} diff --git a/src/main/java/org/leavesmc/leaves/entity/BotManager.java b/src/main/java/org/leavesmc/leaves/entity/BotManager.java new file mode 100644 -index 0000000000000000000000000000000000000000..1724336de0aec0b569d4d3456448c63be3021018 +index 0000000000000000000000000000000000000000..105fc912c2cba717b6533771bfd85eee83954fef --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/BotManager.java -@@ -0,0 +1,62 @@ +@@ -0,0 +1,59 @@ +package org.leavesmc.leaves.entity; + +import org.bukkit.Location; @@ -143,7 +215,6 @@ index 0000000000000000000000000000000000000000..1724336de0aec0b569d4d3456448c63b + +import java.util.Collection; +import java.util.UUID; -+import java.util.function.Consumer; + +/** + * Simple fakeplayer manager @@ -193,15 +264,53 @@ index 0000000000000000000000000000000000000000..1724336de0aec0b569d4d3456448c63b + public boolean unregisterCustomBotAction(String name); + + public BotCreator botCreator(@NotNull String realName, @NotNull Location location); ++} +diff --git a/src/main/java/org/leavesmc/leaves/entity/botaction/BotActionType.java b/src/main/java/org/leavesmc/leaves/entity/botaction/BotActionType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..601a40b28211027db66063681130c0f25fd62152 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/entity/botaction/BotActionType.java +@@ -0,0 +1,34 @@ ++package org.leavesmc.leaves.entity.botaction; + -+ public void createBot(@NotNull BotCreator creator, @Nullable Consumer consumer); ++/** ++ * A Leaves bot action enum ++ */ ++public enum BotActionType { ++ ATTACK("attack"), ++ BREAK("break"), ++ DROP("drop"), ++ FISH("fish"), ++ JUMP("jump"), ++ LOOK("look"), ++ ROTATE("rotate"), ++ ROTATION("rotation"), ++ SNEAK("sneak"), ++ STOP("stop"), ++ SWIM("swim"), ++ USE("use"), ++ USE_ON("use_on"), ++ USE_TO("use_to"), ++ USE_OFFHAND("use_offhand"), ++ USE_ON_OFFHAND("use_on_offhand"), ++ USE_TO_OFFHAND("use_to_offhand"); ++ ++ private final String name; ++ ++ private BotActionType(String name) { ++ this.name = name; ++ } ++ ++ public String getName() { ++ return name; ++ } +} diff --git a/src/main/java/org/leavesmc/leaves/entity/botaction/CustomBotAction.java b/src/main/java/org/leavesmc/leaves/entity/botaction/CustomBotAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..0b1648013d5f03d064c0719c231981082ab563be +index 0000000000000000000000000000000000000000..c952d7ddaf457f79d66baf42557e75600a471f81 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/botaction/CustomBotAction.java -@@ -0,0 +1,52 @@ +@@ -0,0 +1,54 @@ +package org.leavesmc.leaves.entity.botaction; + +import org.bukkit.entity.Player; @@ -231,14 +340,16 @@ index 0000000000000000000000000000000000000000..0b1648013d5f03d064c0719c23198108 + * @param args passed action arguments + * @return a new action instance with given args + */ -+ public @Nullable CustomBotAction getNew(Player player, String[] args); ++ @Nullable ++ public CustomBotAction getNew(@Nullable Player player, String[] args); + + /** + * Requests a list of possible completions for a action argument. + * + * @return A List of a List of possible completions for the argument. + */ -+ public @NotNull List> getTabComplete(); ++ @NotNull ++ public List> getTabComplete(); + + /** + * Return a ticks to wait between {@link CustomBotAction#doTick(Bot)} @@ -256,65 +367,100 @@ index 0000000000000000000000000000000000000000..0b1648013d5f03d064c0719c23198108 +} 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..3437aea093fd67536e73a1068f17167057d935c2 +index 0000000000000000000000000000000000000000..8a73f5cc673a95c434677ad7578abfb5402118e3 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/botaction/LeavesBotAction.java -@@ -0,0 +1,34 @@ +@@ -0,0 +1,73 @@ +package org.leavesmc.leaves.entity.botaction; + -+/** -+ * A Leaves bot action enum -+ */ -+public enum LeavesBotAction { -+ ATTACK("attack"), -+ BREAK("break"), -+ DROP("drop"), -+ FISH("fish"), -+ JUMP("jump"), -+ LOOK("look"), -+ ROTATE("rotate"), -+ ROTATION("rotation"), -+ SNEAK("sneak"), -+ STOP("stop"), -+ SWIM("swim"), -+ USE("use"), -+ USE_ON("use_on"), -+ USE_TO("use_to"), -+ USE_OFFHAND("use_offhand"), -+ USE_ON_OFFHAND("use_on_offhand"), -+ USE_TO_OFFHAND("use_to_offhand"); ++import org.bukkit.entity.Player; ++import org.jetbrains.annotations.Nullable; + -+ private final String name; ++import java.util.UUID; + -+ private LeavesBotAction(String name) { -+ this.name = name; ++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 String getName() { -+ return name; ++ 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..e85efcaef9f3575b6a85d57b02b2d814b21ef6b9 +index 0000000000000000000000000000000000000000..1818aa77f8b051a00b4dbc3da0907cf3462ffcbb --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionEvent.java -@@ -0,0 +1,50 @@ +@@ -0,0 +1,27 @@ +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 BotActionEvent extends BotEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); ++public abstract class BotActionEvent extends BotEvent { + + private final String actionName; + private final UUID actionUUID; -+ private boolean cancel = false; + + public BotActionEvent(@NotNull Bot who, String actionName, UUID actionUUID) { + super(who); @@ -330,12 +476,90 @@ index 0000000000000000000000000000000000000000..e85efcaef9f3575b6a85d57b02b2d814 + 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..7189649e608d41511d4213c1c3938996361290df +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionScheduleEvent.java +@@ -0,0 +1,39 @@ ++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 BotActionScheduleEvent extends BotActionEvent implements Cancellable { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ ++ private boolean cancel = false; ++ ++ public BotActionScheduleEvent(@NotNull Bot who, String actionName, UUID actionUUID) { ++ super(who, actionName, actionUUID); ++ } ++ + @Override + public boolean isCancelled() { + return cancel; @@ -346,16 +570,64 @@ index 0000000000000000000000000000000000000000..e85efcaef9f3575b6a85d57b02b2d814 + this.cancel = cancel; + } + ++ @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..e8de7b19d72b3dfd6e4423096573b3a7ef737803 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/event/bot/BotActionStopEvent.java +@@ -0,0 +1,36 @@ ++package org.leavesmc.leaves.event.bot; ++ ++import org.bukkit.event.HandlerList; ++import org.jetbrains.annotations.NotNull; ++import org.leavesmc.leaves.entity.Bot; ++ ++import java.util.UUID; ++ ++public class BotActionStopEvent extends BotActionEvent { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ ++ public enum Reason { ++ DONE, COMMAND, PLUGIN, INTERNAL ++ } ++ ++ private final Reason reason; ++ ++ public BotActionStopEvent(@NotNull Bot who, String actionName, UUID actionUUID, Reason stopReason) { ++ super(who, actionName, actionUUID); ++ this.reason = stopReason; ++ } ++ ++ public Reason getReason() { ++ return reason; ++ } ++ ++ @Override ++ public @NotNull HandlerList getHandlers() { ++ return handlers; ++ } ++ + public static HandlerList getHandlerList() { + return handlers; + } +} 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..1ebdd3657e615a825918d455691cc1d6df78cfba +index 0000000000000000000000000000000000000000..053be37cb250d77b1c9f4c1bbd83a49da93027a7 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotConfigModifyEvent.java -@@ -0,0 +1,49 @@ +@@ -0,0 +1,50 @@ +package org.leavesmc.leaves.event.bot; + +import org.bukkit.event.Cancellable; @@ -364,6 +636,7 @@ index 0000000000000000000000000000000000000000..1ebdd3657e615a825918d455691cc1d6 +import org.leavesmc.leaves.entity.Bot; + +public class BotConfigModifyEvent extends BotEvent implements Cancellable { ++ + private static final HandlerList handlers = new HandlerList(); + + private final String configName; @@ -387,11 +660,6 @@ index 0000000000000000000000000000000000000000..1ebdd3657e615a825918d455691cc1d6 + } + + @Override -+ public @NotNull HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @Override + public boolean isCancelled() { + return cancel; + } @@ -401,13 +669,18 @@ index 0000000000000000000000000000000000000000..1ebdd3657e615a825918d455691cc1d6 + 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..ec0ed853a6d6dec263784da284ee521c81aebbc8 +index 0000000000000000000000000000000000000000..08e382120feec65c2a842134a1643f236a120bbd --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotCreateEvent.java @@ -0,0 +1,119 @@ @@ -421,19 +694,19 @@ index 0000000000000000000000000000000000000000..ec0ed853a6d6dec263784da284ee521c +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + -+import java.util.Optional; -+ +/** + * Call when a fakeplayer creates a server + */ +public class BotCreateEvent extends Event implements Cancellable { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ + public enum CreateReason { + COMMAND, + PLUGIN, + INTERNAL, + UNKNOWN, + } -+ private static final HandlerList handlers = new HandlerList(); + + private final String bot; + private final String skin; @@ -607,10 +880,10 @@ index 0000000000000000000000000000000000000000..3366b50cf1835129a027b5342e4d8cf0 +} 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..ad358081f1e1da4075243d7ca0a01c1f7b00631b +index 0000000000000000000000000000000000000000..ed9f954da5d381368977eae3ed19a334a3bc3e5a --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotEvent.java -@@ -0,0 +1,31 @@ +@@ -0,0 +1,32 @@ +package org.leavesmc.leaves.event.bot; + +import org.bukkit.event.Event; @@ -621,13 +894,14 @@ index 0000000000000000000000000000000000000000..ad358081f1e1da4075243d7ca0a01c1f + * 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) { // Paper - public ++ public BotEvent(@NotNull final Bot who, boolean async) { + super(async); + bot = who; + } @@ -644,7 +918,7 @@ index 0000000000000000000000000000000000000000..ad358081f1e1da4075243d7ca0a01c1f +} 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..a369b468d4793b36dd0944a1368a70e07b9fc10f +index 0000000000000000000000000000000000000000..8191c0e1302234981212d2fa015425e25825ce61 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotInventoryOpenEvent.java @@ -0,0 +1,46 @@ @@ -658,6 +932,7 @@ index 0000000000000000000000000000000000000000..a369b468d4793b36dd0944a1368a70e0 +import org.leavesmc.leaves.entity.Bot; + +public class BotInventoryOpenEvent extends BotEvent implements Cancellable { ++ + private static final HandlerList handlers = new HandlerList(); + + private final Player player; @@ -683,7 +958,6 @@ index 0000000000000000000000000000000000000000..a369b468d4793b36dd0944a1368a70e0 + return player; + } + -+ + @Override + public @NotNull HandlerList getHandlers() { + return handlers; @@ -696,10 +970,10 @@ index 0000000000000000000000000000000000000000..a369b468d4793b36dd0944a1368a70e0 +} 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..07f6d81c4cd897230bbd6712dac09b8995431104 +index 0000000000000000000000000000000000000000..24e5f4d833897000e0378d4d3c3ff75c08a5bad2 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotJoinEvent.java -@@ -0,0 +1,66 @@ +@@ -0,0 +1,67 @@ +package org.leavesmc.leaves.event.bot; + +import net.kyori.adventure.text.Component; @@ -713,6 +987,7 @@ index 0000000000000000000000000000000000000000..07f6d81c4cd897230bbd6712dac09b89 + * Called when a fakeplayer joins a server + */ +public class BotJoinEvent extends BotEvent { ++ + private static final HandlerList handlers = new HandlerList(); + + private Component joinMessage; @@ -768,10 +1043,10 @@ index 0000000000000000000000000000000000000000..07f6d81c4cd897230bbd6712dac09b89 +} 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..ff3243f420e8c64ea2675e8c8712b851dcce4c27 +index 0000000000000000000000000000000000000000..d4472675af540cdeeebf428144c70b9a5c3f34ce --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotLoadEvent.java -@@ -0,0 +1,58 @@ +@@ -0,0 +1,59 @@ +package org.leavesmc.leaves.event.bot; + +import org.bukkit.event.Cancellable; @@ -785,6 +1060,7 @@ index 0000000000000000000000000000000000000000..ff3243f420e8c64ea2675e8c8712b851 + * Call when a fakeplayer loading a server + */ +public class BotLoadEvent extends Event implements Cancellable { ++ + private static final HandlerList handlers = new HandlerList(); + + private final String bot; @@ -832,10 +1108,10 @@ index 0000000000000000000000000000000000000000..ff3243f420e8c64ea2675e8c8712b851 +} 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..e950643e4b4da99b067d7392034a651361094853 +index 0000000000000000000000000000000000000000..408a7e39ee1923d595fb8ac3f91d60e14a2c446c --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/event/bot/BotRemoveEvent.java -@@ -0,0 +1,107 @@ +@@ -0,0 +1,106 @@ +package org.leavesmc.leaves.event.bot; + +import net.kyori.adventure.text.Component; @@ -847,12 +1123,13 @@ index 0000000000000000000000000000000000000000..e950643e4b4da99b067d7392034a6513 +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.entity.Bot; + -+import java.util.Optional; -+ +/** + * Call when a fakeplayer creates a server + */ +public class BotRemoveEvent extends BotEvent implements Cancellable { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ + public enum RemoveReason { + COMMAND, + PLUGIN, @@ -860,20 +1137,18 @@ index 0000000000000000000000000000000000000000..e950643e4b4da99b067d7392034a6513 + INTERNAL + } + -+ private static final HandlerList handlers = new HandlerList(); -+ + private final RemoveReason reason; + private final CommandSender remover; + private Component removeMessage; -+ private boolean saved; ++ private boolean save; + private boolean cancel = false; + -+ public BotRemoveEvent(@NotNull final Bot who, @NotNull RemoveReason reason, @Nullable CommandSender remover, @Nullable Component removeMessage, boolean saved) { ++ 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.saved = saved; ++ this.save = save; + } + + /** @@ -924,12 +1199,12 @@ index 0000000000000000000000000000000000000000..e950643e4b4da99b067d7392034a6513 + this.cancel = cancel; + } + -+ public boolean isSaved() { -+ return saved; ++ public boolean shouldSave() { ++ return save; + } + -+ public void setSaved(boolean saved) { -+ this.saved = saved; ++ public void setSave(boolean save) { ++ this.save = save; + } + + @Override @@ -945,14 +1220,13 @@ index 0000000000000000000000000000000000000000..e950643e4b4da99b067d7392034a6513 +} 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..434e78be76750adfbeb033ab1aceaae222b978df +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.entity.Player; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.leavesmc.leaves.entity.Bot; @@ -960,6 +1234,7 @@ index 0000000000000000000000000000000000000000..434e78be76750adfbeb033ab1aceaae2 +public class BotSpawnLocationEvent extends BotEvent { + + private static final HandlerList handlers = new HandlerList(); ++ + private Location spawnLocation; + + public BotSpawnLocationEvent(@NotNull final Bot who, @NotNull Location spawnLocation) { diff --git a/patches/server/0010-Fakeplayer-support.patch b/patches/server/0010-Fakeplayer-support.patch index b61bea1a..b8f0cef7 100644 --- a/patches/server/0010-Fakeplayer-support.patch +++ b/patches/server/0010-Fakeplayer-support.patch @@ -626,7 +626,7 @@ index 8dd85b9ca3b3e3429de4d0ec0654982589c6e93e..de9f63fb3b8dcf11a9271794850ce448 return event; diff --git a/src/main/java/org/leavesmc/leaves/bot/BotCommand.java b/src/main/java/org/leavesmc/leaves/bot/BotCommand.java new file mode 100644 -index 0000000000000000000000000000000000000000..ab7c2fb890041c478b18a49beab87d9554cd20c8 +index 0000000000000000000000000000000000000000..a06f24253a2d941c8fd265f50dcaec356b5cfbcc --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/BotCommand.java @@ -0,0 +1,522 @@ @@ -822,7 +822,7 @@ index 0000000000000000000000000000000000000000..ab7c2fb890041c478b18a49beab87d95 + } + } + -+ builder.createAsyncWithSkin(null); ++ builder.spawnWithSkin(null); + } + } + @@ -1154,10 +1154,10 @@ index 0000000000000000000000000000000000000000..ab7c2fb890041c478b18a49beab87d95 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/BotCreateState.java b/src/main/java/org/leavesmc/leaves/bot/BotCreateState.java new file mode 100644 -index 0000000000000000000000000000000000000000..6df68cdbeeed27f4b1cc81a33f70689fae5dd339 +index 0000000000000000000000000000000000000000..ebb3aa94718382e8fac2a8fa617c796a9387709b --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/BotCreateState.java -@@ -0,0 +1,115 @@ +@@ -0,0 +1,120 @@ +package org.leavesmc.leaves.bot; + +import net.minecraft.server.MinecraftServer; @@ -1167,7 +1167,9 @@ index 0000000000000000000000000000000000000000..6df68cdbeeed27f4b1cc81a33f70689f +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.LeavesConfig; ++import org.leavesmc.leaves.entity.Bot; +import org.leavesmc.leaves.entity.BotCreator; ++import org.leavesmc.leaves.entity.CraftBot; +import org.leavesmc.leaves.event.bot.BotCreateEvent; +import org.leavesmc.leaves.plugin.MinecraftInternalPlugin; + @@ -1212,7 +1214,7 @@ index 0000000000000000000000000000000000000000..6df68cdbeeed27f4b1cc81a33f70689f + this.createReason = BotCreateEvent.CreateReason.UNKNOWN; + this.creator = null; + } -+ ++ + public Builder name(@NotNull String name) { + Objects.requireNonNull(name); + this.name = name; @@ -1256,20 +1258,23 @@ index 0000000000000000000000000000000000000000..6df68cdbeeed27f4b1cc81a33f70689f + return new BotCreateState(realName, name, skinName, skin, location, createReason, creator); + } + -+ public void createAsyncWithSkin(Consumer consumer) { -+ Bukkit.getScheduler().runTaskAsynchronously(MinecraftInternalPlugin.INSTANCE, () -> this.mojangAPISkin().create(consumer)); ++ public void spawnWithSkin(Consumer consumer) { ++ Bukkit.getScheduler().runTaskAsynchronously(MinecraftInternalPlugin.INSTANCE, () -> { ++ this.mojangAPISkin(); ++ Bukkit.getScheduler().runTask(MinecraftInternalPlugin.INSTANCE, () -> { ++ CraftBot bot = this.spawn(); ++ if (bot != null && consumer != null) { ++ consumer.accept(bot); ++ } ++ }); ++ }); + } + -+ public void create(Consumer consumer) { ++ @Nullable ++ public CraftBot spawn() { + Objects.requireNonNull(this.location); -+ if (!server.isSameThread()) { -+ server.executeIfPossible(() -> this.create(consumer)); -+ } else { -+ ServerBot bot = this.build().createNow(); -+ if (bot != null && consumer != null) { -+ consumer.accept(bot); -+ } -+ } ++ ServerBot bot = this.build().createNow(); ++ return bot != null ? bot.getBukkitEntity() : null; + } + } +} @@ -1599,7 +1604,7 @@ index 0000000000000000000000000000000000000000..4f5e6e5c1b9d8bd38c98e97fd31b3833 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/BotList.java b/src/main/java/org/leavesmc/leaves/bot/BotList.java new file mode 100644 -index 0000000000000000000000000000000000000000..8357ada4c3a237021df58fee33d66a5a9b0bceaa +index 0000000000000000000000000000000000000000..003f1e01ba56ea2fa9ff311675c58b1d0a38d53b --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/BotList.java @@ -0,0 +1,338 @@ @@ -1829,7 +1834,7 @@ index 0000000000000000000000000000000000000000..8357ada4c3a237021df58fee33d66a5a + bot.doTick(); + } + -+ if (event.isSaved()) { ++ if (event.shouldSave()) { + playerIO.save(bot); + } else { + bot.dropAll(); @@ -2128,10 +2133,10 @@ index 0000000000000000000000000000000000000000..517e3321b866abe9d17a6fe9e919528b +} diff --git a/src/main/java/org/leavesmc/leaves/bot/ServerBot.java b/src/main/java/org/leavesmc/leaves/bot/ServerBot.java new file mode 100644 -index 0000000000000000000000000000000000000000..e48bb6e0e9b745d23637529dd421613d39af3bcd +index 0000000000000000000000000000000000000000..18cf5182c33c43690d7329d6bd435ebc8ee517e5 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/ServerBot.java -@@ -0,0 +1,545 @@ +@@ -0,0 +1,543 @@ +package org.leavesmc.leaves.bot; + +import com.google.common.collect.ImmutableMap; @@ -2186,7 +2191,7 @@ index 0000000000000000000000000000000000000000..e48bb6e0e9b745d23637529dd421613d +import org.leavesmc.leaves.bot.agent.BotConfig; +import org.leavesmc.leaves.bot.agent.Configs; +import org.leavesmc.leaves.entity.CraftBot; -+import org.leavesmc.leaves.event.bot.BotActionEvent; ++import org.leavesmc.leaves.event.bot.BotActionScheduleEvent; +import org.leavesmc.leaves.event.bot.BotCreateEvent; +import org.leavesmc.leaves.event.bot.BotDeathEvent; +import org.leavesmc.leaves.event.bot.BotInventoryOpenEvent; @@ -2642,9 +2647,7 @@ index 0000000000000000000000000000000000000000..e48bb6e0e9b745d23637529dd421613d + return false; + } + -+ BotActionEvent event = new BotActionEvent(this.getBukkitEntity(), action.getName(), action.getUUID()); -+ Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { ++ if (!new BotActionScheduleEvent(this.getBukkitEntity(), action.getName(), action.getUUID()).callEvent()) { + return false; + } + @@ -2987,18 +2990,21 @@ index 0000000000000000000000000000000000000000..a37513e1ba8443c702ab0c01fbe5e052 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e1d6f1500 +index 0000000000000000000000000000000000000000..3ad5484f26e3dc7fb45c5d2ee0687604e6974404 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java -@@ -0,0 +1,138 @@ +@@ -0,0 +1,163 @@ +package org.leavesmc.leaves.bot.agent; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.command.CommandArgument; +import org.leavesmc.leaves.command.CommandArgumentResult; ++import org.leavesmc.leaves.event.bot.BotActionExecuteEvent; ++import org.leavesmc.leaves.event.bot.BotActionStopEvent; + +import java.util.List; +import java.util.UUID; @@ -3018,7 +3024,6 @@ index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e + private int needWaitTick; + private int canDoNumber; + -+ + public BotAction(String name, CommandArgument argument, Supplier creator) { + this.name = name; + this.argument = argument; @@ -3064,6 +3069,10 @@ index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e + return (E) this; + } + ++ public int getCanDoNumber() { ++ return this.canDoNumber; ++ } ++ + public boolean isCancelled() { + return cancel; + } @@ -3072,6 +3081,11 @@ index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e + this.cancel = cancel; + } + ++ public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) { ++ new BotActionStopEvent(bot.getBukkitEntity(), this.name, this.uuid, reason).callEvent(); ++ this.setCancelled(true); ++ } ++ + public CommandArgument getArgument() { + return this.argument; + } @@ -3084,11 +3098,25 @@ index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e + + public void tryTick(ServerBot bot) { + if (this.canDoNumber == 0) { -+ this.setCancelled(true); ++ this.stop(bot, BotActionStopEvent.Reason.DONE); + return; + } + + if (this.needWaitTick <= 0) { ++ BotActionExecuteEvent event = new BotActionExecuteEvent(bot.getBukkitEntity(), name, uuid); ++ ++ event.callEvent(); ++ if (event.getResult() == BotActionExecuteEvent.Result.SOFT_CANCEL) { ++ this.needWaitTick = this.getTickDelay(); ++ return; ++ } else if (event.getResult() == BotActionExecuteEvent.Result.HARD_CANCEL) { ++ if (this.canDoNumber > 0) { ++ this.canDoNumber--; ++ } ++ this.needWaitTick = this.getTickDelay(); ++ return; ++ } ++ + if (this.doTick(bot)) { + if (this.canDoNumber > 0) { + this.canDoNumber--; @@ -3125,7 +3153,7 @@ index 0000000000000000000000000000000000000000..55b981f5d56c5df26f2709c0450bb39e + this.uuid = nbt.getUUID("actionUUID"); + } + -+ public abstract void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result); ++ public abstract void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result); + + public abstract boolean doTick(@NotNull ServerBot bot); +} @@ -3249,14 +3277,15 @@ index 0000000000000000000000000000000000000000..d99f459b2e323474174cfd5d892cb757 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/AbstractTimerAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/AbstractTimerAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..c197dd7726c9f3224b8fe44dac9c707155019c3b +index 0000000000000000000000000000000000000000..be55a3085a53542c08e7f0209883a4f3f72602e7 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/AbstractTimerAction.java -@@ -0,0 +1,24 @@ +@@ -0,0 +1,25 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; +import org.leavesmc.leaves.command.CommandArgumentResult; @@ -3273,7 +3302,7 @@ index 0000000000000000000000000000000000000000..c197dd7726c9f3224b8fe44dac9c7071 + } + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + this.setTickDelay(result.readInt(20)).setNumber(result.readInt(-1)); + } +} @@ -3386,17 +3415,78 @@ index 0000000000000000000000000000000000000000..bf7d20374cd7bff7cb7e09d209c6da5d + return f; + } +} +diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftBotAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftBotAction.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d96fc7b97ff826efe1bd36988f2d1a9ea04654cb +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftBotAction.java +@@ -0,0 +1,54 @@ ++package org.leavesmc.leaves.bot.agent.actions; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.NotNull; ++import org.leavesmc.leaves.bot.agent.Actions; ++import org.leavesmc.leaves.bot.agent.BotAction; ++import org.leavesmc.leaves.entity.botaction.BotActionType; ++import org.leavesmc.leaves.entity.botaction.LeavesBotAction; ++ ++public class CraftBotAction extends LeavesBotAction { ++ ++ private final BotAction handle; ++ ++ public CraftBotAction(@NotNull BotAction action) { ++ super(BotActionType.valueOf(action.getName()), action.getTickDelay(), action.getCanDoNumber()); ++ this.handle = action; ++ } ++ ++ @Contract("_ -> new") ++ @NotNull ++ public static LeavesBotAction asAPICopy(BotAction action) { ++ return new CraftBotAction(action); ++ } ++ ++ @NotNull ++ public static BotAction asInternalCopy(@NotNull LeavesBotAction action) { ++ BotAction act = Actions.getForName(action.getActionName()); ++ if (act == null) { ++ throw new IllegalArgumentException("Invalid action name!"); ++ } ++ ++ BotAction newAction = null; ++ String[] args = new String[]{String.valueOf(action.getExecuteInterval()), String.valueOf(action.getRemainingExecuteTime())}; ++ try { ++ if (act instanceof CraftCustomBotAction customBotAction) { ++ newAction = customBotAction.createCraft(action.getActionPlayer(), args); ++ } else { ++ newAction = act.create(); ++ newAction.loadCommand(action.getActionPlayer() == null ? null : ((CraftPlayer) action.getActionPlayer()).getHandle(), act.getArgument().parse(0, args)); ++ } ++ } catch (IllegalArgumentException ignore) { ++ } ++ ++ if (newAction == null) { ++ throw new IllegalArgumentException("Invalid action!"); // TODO look action ++ } ++ return newAction; ++ } ++ ++ public BotAction getHandle() { ++ return handle; ++ } ++} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..35cbb218df21bee28f3b3b1dcb09c29e617ddb6d +index 0000000000000000000000000000000000000000..7b149243b08a44f1181e82217a8645ccab7732d7 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java -@@ -0,0 +1,48 @@ +@@ -0,0 +1,49 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3413,11 +3503,11 @@ index 0000000000000000000000000000000000000000..35cbb218df21bee28f3b3b1dcb09c29e + } + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + throw new UnsupportedOperationException("Not supported."); + } + -+ public CraftCustomBotAction createCraft(@NotNull Player player, String[] args) { ++ public CraftCustomBotAction createCraft(@Nullable Player player, String[] args) { + CustomBotAction newRealAction = realAction.getNew(player, args); + if (newRealAction != null) { + return new CraftCustomBotAction(this.getName(), newRealAction); @@ -3442,14 +3532,15 @@ index 0000000000000000000000000000000000000000..35cbb218df21bee28f3b3b1dcb09c29e +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..b0a86493a2567ee8278aed27797b41eba1343660 +index 0000000000000000000000000000000000000000..c71e483e8938ef3b181c95d8e297e54203b5b914 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java -@@ -0,0 +1,24 @@ +@@ -0,0 +1,25 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.command.CommandArgumentResult; + @@ -3460,7 +3551,7 @@ index 0000000000000000000000000000000000000000..b0a86493a2567ee8278aed27797b41eb + } + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + this.setTickDelay(result.readInt(100)).setNumber(result.readInt(1)); + } + @@ -3578,16 +3669,17 @@ index 0000000000000000000000000000000000000000..6fc9ba9bf94cb19ed32cfafa3a44fad0 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..edd2cb2cd53e742c7ad1916a57a6f9555c37a58f +index 0000000000000000000000000000000000000000..8be962cf7dc273ccb6a6754684a9be8353865225 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java -@@ -0,0 +1,62 @@ +@@ -0,0 +1,63 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3608,7 +3700,7 @@ index 0000000000000000000000000000000000000000..edd2cb2cd53e742c7ad1916a57a6f955 + private Vector pos; + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) throws IllegalArgumentException { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) throws IllegalArgumentException { + Vector pos = result.readVector(); + if (pos != null) { + this.setPos(pos).setTickDelay(0).setNumber(1); @@ -3646,15 +3738,16 @@ index 0000000000000000000000000000000000000000..edd2cb2cd53e742c7ad1916a57a6f955 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..8e4740ac745af67029443719c083258b4f261718 +index 0000000000000000000000000000000000000000..84eb7bd727a0085d005a6ee518dfbb8b44fce991 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java -@@ -0,0 +1,50 @@ +@@ -0,0 +1,51 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3669,7 +3762,7 @@ index 0000000000000000000000000000000000000000..8e4740ac745af67029443719c083258b + private ServerPlayer player; + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + this.setPlayer(player).setTickDelay(0).setNumber(1); + } + @@ -3702,15 +3795,16 @@ index 0000000000000000000000000000000000000000..8e4740ac745af67029443719c083258b +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..78d7cf38c28c5512955cf2c59a78ac493e99f829 +index 0000000000000000000000000000000000000000..671d1aada7aa3cac0f3df8eec235b2f1ae389492 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java -@@ -0,0 +1,60 @@ +@@ -0,0 +1,65 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3731,7 +3825,11 @@ index 0000000000000000000000000000000000000000..78d7cf38c28c5512955cf2c59a78ac49 + private float pitch; + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { ++ if (player == null) { ++ return; ++ } ++ + this.setYaw(result.readFloat(player.getYRot())).setPitch(result.readFloat(player.getXRot())).setTickDelay(0).setNumber(1); + } + @@ -3768,14 +3866,15 @@ index 0000000000000000000000000000000000000000..78d7cf38c28c5512955cf2c59a78ac49 +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..6cfc1986aa3fc895ba854226170555dc2583d7fa +index 0000000000000000000000000000000000000000..923cf55d81fce5cf9db9a1c7adc6f3aed5753b16 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java -@@ -0,0 +1,26 @@ +@@ -0,0 +1,27 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3788,7 +3887,7 @@ index 0000000000000000000000000000000000000000..6cfc1986aa3fc895ba854226170555dc + } + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + this.setTickDelay(0).setNumber(1); + } + @@ -3800,15 +3899,16 @@ index 0000000000000000000000000000000000000000..6cfc1986aa3fc895ba854226170555dc +} diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java new file mode 100644 -index 0000000000000000000000000000000000000000..b51658329ee851c62e74000631e79185baf5eee0 +index 0000000000000000000000000000000000000000..b5ccedee17857bc955301512ee965d81fd12017f --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java -@@ -0,0 +1,29 @@ +@@ -0,0 +1,30 @@ +package org.leavesmc.leaves.bot.agent.actions; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.bot.agent.BotAction; +import org.leavesmc.leaves.command.CommandArgument; @@ -3821,7 +3921,7 @@ index 0000000000000000000000000000000000000000..b51658329ee851c62e74000631e79185 + } + + @Override -+ public void loadCommand(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) { ++ public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) { + this.setTickDelay(0).setNumber(-1); + } + @@ -4256,10 +4356,10 @@ index 0000000000000000000000000000000000000000..a3f978318a67c3c5e147a50eb2b6c01c +} diff --git a/src/main/java/org/leavesmc/leaves/entity/CraftBot.java b/src/main/java/org/leavesmc/leaves/entity/CraftBot.java new file mode 100644 -index 0000000000000000000000000000000000000000..4d44a49abd5bdeca978d8dcf00a15023ebe02666 +index 0000000000000000000000000000000000000000..744f5533859bfe81267b3fd5133597e80e0fdc73 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/CraftBot.java -@@ -0,0 +1,53 @@ +@@ -0,0 +1,84 @@ +package org.leavesmc.leaves.entity; + +import org.bukkit.craftbukkit.CraftServer; @@ -4268,6 +4368,10 @@ index 0000000000000000000000000000000000000000..4d44a49abd5bdeca978d8dcf00a15023 +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.bot.BotList; +import org.leavesmc.leaves.bot.ServerBot; ++import org.leavesmc.leaves.bot.agent.BotAction; ++import org.leavesmc.leaves.bot.agent.actions.CraftBotAction; ++import org.leavesmc.leaves.entity.botaction.LeavesBotAction; ++import org.leavesmc.leaves.event.bot.BotActionStopEvent; +import org.leavesmc.leaves.event.bot.BotRemoveEvent; + +import java.util.UUID; @@ -4294,6 +4398,33 @@ index 0000000000000000000000000000000000000000..4d44a49abd5bdeca978d8dcf00a15023 + } + + @Override ++ public void addAction(@NotNull LeavesBotAction action) { ++ this.getHandle().addBotAction(CraftBotAction.asInternalCopy(action)); ++ } ++ ++ @Override ++ public LeavesBotAction getAction(int index) { ++ return CraftBotAction.asAPICopy(this.getHandle().getBotActions().get(index)); ++ } ++ ++ @Override ++ public int getActionSize() { ++ return this.getHandle().getBotActions().size(); ++ } ++ ++ @Override ++ public void stopAction(int index) { ++ this.getHandle().getBotActions().get(index).stop(this.getHandle(), BotActionStopEvent.Reason.PLUGIN); ++ } ++ ++ @Override ++ public void stopAllActions() { ++ for (BotAction action : this.getHandle().getBotActions()) { ++ action.stop(this.getHandle(), BotActionStopEvent.Reason.PLUGIN); ++ } ++ } ++ ++ @Override + public boolean remove(boolean save) { + BotList.INSTANCE.removeBot(this.getHandle(), BotRemoveEvent.RemoveReason.PLUGIN, null, save); + return true; @@ -4315,10 +4446,10 @@ index 0000000000000000000000000000000000000000..4d44a49abd5bdeca978d8dcf00a15023 +} diff --git a/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java b/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java new file mode 100644 -index 0000000000000000000000000000000000000000..e748d49d329ba814e3906a8bd9cba017653be51b +index 0000000000000000000000000000000000000000..422640df346ccae612b2d3492780efa59d8b4d17 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java -@@ -0,0 +1,92 @@ +@@ -0,0 +1,80 @@ +package org.leavesmc.leaves.entity; + +import com.google.common.base.Function; @@ -4339,7 +4470,6 @@ index 0000000000000000000000000000000000000000..e748d49d329ba814e3906a8bd9cba017 +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; -+import java.util.function.Consumer; + +public class CraftBotManager implements BotManager { + @@ -4397,18 +4527,7 @@ index 0000000000000000000000000000000000000000..e748d49d329ba814e3906a8bd9cba017 + + @Override + public BotCreator botCreator(@NotNull String realName, @NotNull Location location) { -+ return BotCreateState.builder(realName, location); -+ } -+ -+ @Override -+ public void createBot(@NotNull BotCreator creator, @Nullable Consumer consumer) { -+ if (creator instanceof BotCreateState.Builder builder) { -+ builder.createReason(BotCreateEvent.CreateReason.PLUGIN).create((serverBot) -> { -+ if (consumer != null && serverBot != null) { -+ consumer.accept(serverBot.getBukkitEntity()); -+ } -+ }); -+ } ++ return BotCreateState.builder(realName, location).createReason(BotCreateEvent.CreateReason.PLUGIN); + } +} diff --git a/src/main/java/org/leavesmc/leaves/plugin/MinecraftInternalPlugin.java b/src/main/java/org/leavesmc/leaves/plugin/MinecraftInternalPlugin.java