introduce 26.1.2 changes
Fakeplayer Action Gui - Stage 1 (#161) Still needs some tweaks to fit Leaves' changes * pre add gui (not implemented) * fallback one change in Actions * pre for new structure of gui register * Enhance GUI node structure with item support and command handling * feat: update GUI node handling in bot actions * feat: enhance command building logic in GUI nodes * fix a bug * feat: implement GUI navigation and command execution in BotAction GUI * feat: refactor GuiNode and related classes to use Item instead of ItemStack * feat: update command building logic to include additional action parameters * feat: simplify command syntax in BotAction GUI and update inventory view handling * feat: add ActionType enum and implement action confirmation in BotAction GUI * feat: enhance ActionType handling and selection in BotAction GUI * feat: integrate GUI data handling in bot actions and enhance navigation * fix up * because of tmp move to new org, rebrand * Implement pagination for bot action GUI and enhance action stop functionality * ci updates * replace boarder item * fix https://github.com/LophineCraft/Lophine/pull/161#issuecomment-5012127501 issues - 1 * fix https://github.com/LophineCraft/Lophine/pull/161#issuecomment-5012127501 issues - 2 * fix index * replace some items * feat: use the same item shown for stop & start action * remove 2 todos, we wouldn't provide them * refactor: simplify rendering methods in BotActionGuiContainer * feat: add empty action placeholder and simplify item rendering * fix: remove extra suggestions * fix: update GUI icons for jump, move, and swap actions * fix: update comment in open action gui * set release mode to true --------- Co-authored-by: xiaoxijun <A3167717663@hotmail.com>
This commit is contained in:
@@ -0,0 +1,759 @@
|
||||
package fun.bm.lophine.bot;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.ActionType;
|
||||
import fun.bm.lophine.bot.action.gui.GuiNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.agent.actions.AbstractBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
import org.leavesmc.leaves.entity.bot.action.BotAction;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftBotAction;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class BotActionGuiContainer extends SimpleContainer {
|
||||
private static final Map<String, GuiRootNode> GUI_ROOT_NODE_MAP = new LinkedHashMap<>();
|
||||
|
||||
private static final int CONTAINER_SIZE = 54;
|
||||
|
||||
private static final int[] BORDER_SLOTS = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 17,
|
||||
18, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43, 44,
|
||||
45, 46, 47, 48, 49, 50, 51, 52, 53
|
||||
};
|
||||
|
||||
public static final int[] CONTENT_SLOTS = {
|
||||
10, 11, 12, 13, 14, 15, 16,
|
||||
19, 20, 21, 22, 23, 24, 25
|
||||
};
|
||||
|
||||
private static final int BACK_BUTTON_SLOT = 38;
|
||||
private static final int COMMAND_BUILDER_SLOT = 40;
|
||||
private static final int HOME_BUTTON_SLOT = 42;
|
||||
private static final int PREV_PAGE_SLOT = 39;
|
||||
private static final int NEXT_PAGE_SLOT = 41;
|
||||
|
||||
private static final ItemStack boarder = new ItemStack(Items.STAINED_GLASS_PANE.white());
|
||||
private static final ItemStack emptyActionPlaceholder = createEmptyActionPlaceholder();
|
||||
|
||||
private final CraftBot bot;
|
||||
private final CraftPlayer player;
|
||||
private final Deque<GuiNode> navigationStack = new ArrayDeque<>();
|
||||
private final Deque<Boolean> fromRootLevelStack = new ArrayDeque<>(); // Track if we came from root level
|
||||
private GuiNode currentNode = null;
|
||||
|
||||
// New state for action type selection flow
|
||||
private ActionType selectedActionType = null;
|
||||
private boolean isSelectingActionType = false;
|
||||
|
||||
// Pagination state
|
||||
private int currentPage = 0;
|
||||
|
||||
public static void registerGuiRootNode(GuiRootNode guiRootNode) {
|
||||
if (!GUI_ROOT_NODE_MAP.containsKey(guiRootNode.getName())) {
|
||||
GUI_ROOT_NODE_MAP.put(guiRootNode.getName(), guiRootNode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregisterGuiRootNode(String name) {
|
||||
GUI_ROOT_NODE_MAP.remove(name);
|
||||
}
|
||||
|
||||
public static GuiRootNode getGuiRootNode(String name) {
|
||||
return GUI_ROOT_NODE_MAP.get(name);
|
||||
}
|
||||
|
||||
public static Map<String, GuiRootNode> getGuiRootNodes() {
|
||||
return new LinkedHashMap<>(GUI_ROOT_NODE_MAP);
|
||||
}
|
||||
|
||||
public BotActionGuiContainer(@NotNull CraftBot bot, CraftPlayer player) {
|
||||
super(CONTAINER_SIZE, player);
|
||||
this.bot = bot;
|
||||
this.player = player;
|
||||
this.showActionTypes();
|
||||
}
|
||||
|
||||
public boolean isCommandBuilderSlot(int id) {
|
||||
return id == COMMAND_BUILDER_SLOT;
|
||||
}
|
||||
|
||||
private void fillBorder() {
|
||||
for (int slot : BORDER_SLOTS) {
|
||||
this.setItem(slot, boarder);
|
||||
}
|
||||
}
|
||||
|
||||
private void showActionTypes() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.selectedActionType = null;
|
||||
this.isSelectingActionType = true;
|
||||
this.currentPage = 0;
|
||||
|
||||
this.renderActionTypes();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
private void showRootNodes() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.isSelectingActionType = false;
|
||||
this.currentPage = 0;
|
||||
|
||||
List<GuiRootNode> allNodes = new ArrayList<>(GUI_ROOT_NODE_MAP.values());
|
||||
if (allNodes.isEmpty()) {
|
||||
// No root nodes, go back to action type selection
|
||||
this.showActionTypes();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderRootNodes();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
public void navigateToChild(GuiNode node) {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're at root level (currentNode is null but not selecting action type),
|
||||
// we need to track that we came from root nodes level
|
||||
if (this.currentNode != null) {
|
||||
this.navigationStack.push(this.currentNode);
|
||||
this.fromRootLevelStack.push(false);
|
||||
} else if (!this.isSelectingActionType && this.selectedActionType != null) {
|
||||
// We're at root nodes level, mark that we should return to root nodes
|
||||
this.fromRootLevelStack.push(true);
|
||||
}
|
||||
|
||||
this.currentNode = node;
|
||||
this.currentPage = 0;
|
||||
this.refreshContainer();
|
||||
}
|
||||
|
||||
public boolean navigateBack() {
|
||||
// If we're at root node level (no navigation stack), go back to action type selection
|
||||
if (this.navigationStack.isEmpty() && this.fromRootLevelStack.isEmpty()) {
|
||||
// If we have a selected action type, we're at the root nodes level
|
||||
if (!this.isSelectingActionType && this.selectedActionType != null) {
|
||||
this.showActionTypes();
|
||||
return true;
|
||||
}
|
||||
// Already at action type selection
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we should return to root nodes level
|
||||
boolean fromRootLevel = !this.fromRootLevelStack.isEmpty() && this.fromRootLevelStack.pop();
|
||||
|
||||
if (fromRootLevel) {
|
||||
// Return to root nodes level
|
||||
this.currentNode = null;
|
||||
this.showRootNodes();
|
||||
} else if (!this.navigationStack.isEmpty()) {
|
||||
// Pop from navigation stack and refresh
|
||||
this.currentNode = this.navigationStack.pop();
|
||||
this.refreshContainer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void navigateHome() {
|
||||
this.showActionTypes();
|
||||
}
|
||||
|
||||
private void refreshContainer() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
if (children == null || children.isEmpty()) {
|
||||
// Empty page: auto-navigate back
|
||||
this.navigateBack();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderCurrentNodeChildren();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<GuiNode> getChildrenOfCurrentNode() {
|
||||
if (this.currentNode == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.currentNode instanceof GuiRootNode rootNode) {
|
||||
return rootNode.getChildren();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderActionTypes() {
|
||||
List<ActionType> allTypes = List.of(ActionType.values());
|
||||
this.renderPagedItems(allTypes, (actionType, slot) -> {
|
||||
ItemStack item = actionType.toConfirmNode(null).getItemStack();
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderRootNodes() {
|
||||
List<GuiRootNode> allNodes = new ArrayList<>(GUI_ROOT_NODE_MAP.values());
|
||||
this.renderPagedItems(allNodes, (node, slot) -> {
|
||||
ItemStack item = createNodeItemWithRunHint(node);
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderCurrentNodeChildren() {
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
if (children != null && !children.isEmpty()) {
|
||||
List<GuiNode> childList = new ArrayList<>(children);
|
||||
this.renderPagedItems(childList, (node, slot) -> {
|
||||
ItemStack item = createNodeItemWithRunHint(node);
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void renderBotActions() {
|
||||
int actionSize = this.bot.getActionSize();
|
||||
if (actionSize > 0) {
|
||||
List<Integer> actionIndices = new ArrayList<>();
|
||||
for (int i = 0; i < actionSize; i++) {
|
||||
actionIndices.add(i);
|
||||
}
|
||||
this.renderPagedItems(actionIndices, (index, slot) -> {
|
||||
BotAction<?> action = this.bot.getAction(index);
|
||||
ItemStack item = createActionItem(action, index);
|
||||
if (!item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Show empty placeholder when no actions are active
|
||||
this.setItem(CONTENT_SLOTS[0], emptyActionPlaceholder);
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemStack createEmptyActionPlaceholder() {
|
||||
ItemStack item = new ItemStack(Items.STRUCTURE_VOID);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§cNo active actions"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(
|
||||
Component.literal("§cThis bot has no running actions"),
|
||||
Component.literal("§cUse START to add new actions")
|
||||
)));
|
||||
return item;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GuiNode getGuiNodeAtSlot(int slot) {
|
||||
if (slot == BACK_BUTTON_SLOT && this.canNavigateBack()) {
|
||||
return null; // back button is handled separately
|
||||
}
|
||||
|
||||
// Check if slot is a border slot
|
||||
for (int borderSlot : BORDER_SLOTS) {
|
||||
if (borderSlot == slot) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Set<GuiNode> nodes = this.getCurrentDisplayNodes();
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the content index for this slot
|
||||
int contentIndex = -1;
|
||||
for (int i = 0; i < CONTENT_SLOTS.length; i++) {
|
||||
if (CONTENT_SLOTS[i] == slot) {
|
||||
contentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Apply pagination offset
|
||||
int actualIndex = this.currentPage * CONTENT_SLOTS.length + contentIndex;
|
||||
int index = 0;
|
||||
for (GuiNode node : nodes) {
|
||||
if (index == actualIndex) {
|
||||
return node;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isBackButtonSlot(int slot) {
|
||||
return slot == BACK_BUTTON_SLOT && this.canNavigateBack();
|
||||
}
|
||||
|
||||
public boolean isHomeButtonSlot(int slot) {
|
||||
return slot == HOME_BUTTON_SLOT && this.canNavigateHome();
|
||||
}
|
||||
|
||||
private static ItemStack createBackButtonItem() {
|
||||
ItemStack item = new ItemStack(Items.WOOL.red());
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§cBack"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(Component.literal("Return to previous page"))));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createHomeButtonItem() {
|
||||
ItemStack item = new ItemStack(Items.BED.red());
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§aHome"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(Component.literal("Return to main menu"))));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ItemStack createNodeItemWithRunHint(GuiNode node) {
|
||||
ItemStack itemStack = node.getItemStack();
|
||||
|
||||
// Check if this is a last-level node (no children)
|
||||
boolean isLastLevel;
|
||||
if (node instanceof GuiRootNode rootNode) {
|
||||
isLastLevel = rootNode.getChildren().isEmpty();
|
||||
} else {
|
||||
// Non-RootNode is always considered last level
|
||||
isLastLevel = true;
|
||||
}
|
||||
|
||||
// If it's the last level and we have an action type selected, add run hint
|
||||
if (isLastLevel && this.selectedActionType != null && !this.isSelectingActionType) {
|
||||
// Get existing lore or create new one
|
||||
ItemLore existingLore = itemStack.get(DataComponents.LORE);
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
|
||||
if (existingLore != null) {
|
||||
loreLines.addAll(existingLore.lines());
|
||||
}
|
||||
|
||||
// Add golden run hint
|
||||
loreLines.add(Component.literal("§6Click to execute"));
|
||||
|
||||
itemStack.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render items with pagination support.
|
||||
*
|
||||
* @param items the full list of items
|
||||
* @param renderer callback to render each item at a specific slot
|
||||
*/
|
||||
private <T> void renderPagedItems(List<T> items, PagedItemRenderer<T> renderer) {
|
||||
int pageSize = CONTENT_SLOTS.length;
|
||||
int totalPages = Math.max(1, (items.size() + pageSize - 1) / pageSize);
|
||||
|
||||
// Clamp current page
|
||||
if (this.currentPage >= totalPages) {
|
||||
this.currentPage = totalPages - 1;
|
||||
}
|
||||
if (this.currentPage < 0) {
|
||||
this.currentPage = 0;
|
||||
}
|
||||
|
||||
int start = this.currentPage * pageSize;
|
||||
int end = Math.min(start + pageSize, items.size());
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
int slotIndex = i - start;
|
||||
renderer.render(items.get(i), CONTENT_SLOTS[slotIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface PagedItemRenderer<T> {
|
||||
void render(T item, int slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add navigation buttons including pagination controls.
|
||||
*/
|
||||
private void addNavigationButtons() {
|
||||
if (this.canNavigateBack()) {
|
||||
this.setItem(BACK_BUTTON_SLOT, createBackButtonItem());
|
||||
}
|
||||
|
||||
this.setItem(COMMAND_BUILDER_SLOT, createCommandBuilderItem());
|
||||
|
||||
if (this.canNavigateHome()) {
|
||||
this.setItem(HOME_BUTTON_SLOT, createHomeButtonItem());
|
||||
}
|
||||
|
||||
// Pagination buttons
|
||||
if (this.hasPrevPage()) {
|
||||
this.setItem(PREV_PAGE_SLOT, createPrevPageItem());
|
||||
}
|
||||
if (this.hasNextPage()) {
|
||||
this.setItem(NEXT_PAGE_SLOT, createNextPageItem());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPrevPage() {
|
||||
return this.currentPage > 0;
|
||||
}
|
||||
|
||||
private boolean hasNextPage() {
|
||||
int totalItems = this.getCurrentTotalItemCount();
|
||||
int totalPages = Math.max(1, (totalItems + CONTENT_SLOTS.length - 1) / CONTENT_SLOTS.length);
|
||||
return this.currentPage < totalPages - 1;
|
||||
}
|
||||
|
||||
private int getCurrentTotalItemCount() {
|
||||
if (this.isSelectingActionType) {
|
||||
return ActionType.values().length;
|
||||
}
|
||||
if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
return this.bot.getActionSize();
|
||||
}
|
||||
if (this.currentNode == null) {
|
||||
return GUI_ROOT_NODE_MAP.size();
|
||||
}
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
return children != null ? children.size() : 0;
|
||||
}
|
||||
|
||||
public void prevPage() {
|
||||
if (this.hasPrevPage()) {
|
||||
this.currentPage--;
|
||||
this.redisplayCurrentViewWithoutReset();
|
||||
}
|
||||
}
|
||||
|
||||
public void nextPage() {
|
||||
if (this.hasNextPage()) {
|
||||
this.currentPage++;
|
||||
this.redisplayCurrentViewWithoutReset();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPrevPageSlot(int slot) {
|
||||
return slot == PREV_PAGE_SLOT && this.hasPrevPage();
|
||||
}
|
||||
|
||||
public boolean isNextPageSlot(int slot) {
|
||||
return slot == NEXT_PAGE_SLOT && this.hasNextPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-render the current view without resetting pagination state.
|
||||
* Used for page navigation to preserve currentPage value.
|
||||
*/
|
||||
private void redisplayCurrentViewWithoutReset() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
|
||||
if (this.isSelectingActionType) {
|
||||
this.renderActionTypes();
|
||||
} else if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
this.renderBotActions();
|
||||
} else if (this.currentNode != null) {
|
||||
this.renderCurrentNodeChildren();
|
||||
} else {
|
||||
this.renderRootNodes();
|
||||
}
|
||||
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
private static ItemStack createPrevPageItem() {
|
||||
ItemStack item = new ItemStack(Items.ARROW);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§ePrevious Page"));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createNextPageItem() {
|
||||
ItemStack item = new ItemStack(Items.ARROW);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§eNext Page"));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ItemStack createCommandBuilderItem() {
|
||||
ItemStack item = new ItemStack(Items.BOOK);
|
||||
|
||||
// Build the complete command preview
|
||||
String commandPreview = buildCommandPreview();
|
||||
|
||||
// Check if current node is runnable (confirmable)
|
||||
boolean canRun = this.currentNode != null && this.currentNode.isConfirmable();
|
||||
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
loreLines.add(Component.literal("§eCommand:"));
|
||||
loreLines.add(Component.literal("§f" + commandPreview));
|
||||
|
||||
// Add run hint if the node is confirmable
|
||||
if (canRun) {
|
||||
loreLines.add(Component.literal("§6Click to execute"));
|
||||
}
|
||||
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§6Command Builder"));
|
||||
item.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
return item;
|
||||
}
|
||||
|
||||
private String buildCommandPreview() {
|
||||
if (this.selectedActionType == null) {
|
||||
return "Select an action type first";
|
||||
}
|
||||
|
||||
// For STOP action type, show different preview
|
||||
if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
int actionSize = this.bot.getActionSize();
|
||||
if (actionSize == 0) {
|
||||
return "No actions to stop";
|
||||
}
|
||||
return "Click an action to stop it (" + actionSize + " active)";
|
||||
}
|
||||
|
||||
try {
|
||||
String actionPrefix = this.selectedActionType.getCommandActionPrefix();
|
||||
String actionSuffix = this.selectedActionType.getCommandActionSuffix();
|
||||
if (!actionSuffix.isEmpty()) {
|
||||
actionSuffix = actionSuffix + " ";
|
||||
}
|
||||
|
||||
String extra = actionPrefix + " " + this.bot.getName() + " " + actionSuffix;
|
||||
|
||||
// If we have a current node and it's a GuiRootNode, build the command
|
||||
if (this.currentNode instanceof GuiRootNode rootNode) {
|
||||
String command = rootNode.buildCommand(extra);
|
||||
|
||||
// Apply parameter limit based on ActionType
|
||||
if (this.selectedActionType.getMaxAllowedParameters() == 0) {
|
||||
command = actionPrefix + " " + this.bot.getName();
|
||||
}
|
||||
|
||||
return command;
|
||||
} else if (!this.isSelectingActionType) {
|
||||
// At root nodes level or selecting action type
|
||||
return extra.trim() + " <command>";
|
||||
} else {
|
||||
return "Select a command node";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "Error building command";
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<GuiNode> getCurrentDisplayNodes() {
|
||||
// If selecting action type, return null (handled separately)
|
||||
if (this.isSelectingActionType) {
|
||||
return null;
|
||||
}
|
||||
if (this.currentNode == null) {
|
||||
return new LinkedHashSet<>(GUI_ROOT_NODE_MAP.values());
|
||||
}
|
||||
return this.getChildrenOfCurrentNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user selects an ActionType (START/STOP)
|
||||
*/
|
||||
public void selectActionType(ActionType actionType) {
|
||||
this.selectedActionType = actionType;
|
||||
this.isSelectingActionType = false;
|
||||
|
||||
// If STOP action type is selected, show current bot actions instead of root nodes
|
||||
if (actionType == ActionType.ACTION_STOP) {
|
||||
this.showCurrentBotActions();
|
||||
} else {
|
||||
this.showRootNodes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the current bot's scheduled actions for stopping
|
||||
*/
|
||||
public void showCurrentBotActions() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.isSelectingActionType = false;
|
||||
this.currentPage = 0;
|
||||
|
||||
this.renderBotActions();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ItemStack representing a bot action for display in the GUI
|
||||
*/
|
||||
private ItemStack createActionItem(BotAction<?> action, int index) {
|
||||
// Use the same item model as registered in GuiRootNode (start action display)
|
||||
AbstractBotAction<?> handle = ((CraftBotAction<?, ?>) action).getHandle();
|
||||
GuiRootNode guiData = handle.getGuiData();
|
||||
ItemStack item = guiData != null ? guiData.getItemStack() : new ItemStack(Items.PAPER);
|
||||
String actionName = action.getName();
|
||||
String actionHash = action.getUUID().toString();
|
||||
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§c" + actionName));
|
||||
|
||||
// Store action hash in CUSTOM_DATA for later retrieval
|
||||
CompoundTag customTag = new CompoundTag();
|
||||
customTag.putString("action_hash", actionHash);
|
||||
item.set(DataComponents.CUSTOM_DATA, CustomData.of(customTag));
|
||||
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
((CraftBotAction<?, ?>) action).getHandle().save(nbt);
|
||||
nbt.forEach((key, tag) -> loreLines.add(Component.literal("§7" + key + ": §f" + tag)));
|
||||
loreLines.add(Component.literal("§7Index: §f" + index));
|
||||
loreLines.add(Component.literal("§7Hash: §f" + actionHash.substring(0, 8)));
|
||||
loreLines.add(Component.literal("§6Click to stop this action"));
|
||||
|
||||
item.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action hash (UUID string) at a specific slot (for STOP action type)
|
||||
* Returns null if not found or not in STOP mode
|
||||
*/
|
||||
@Nullable
|
||||
public String getActionHashAtSlot(int slot) {
|
||||
if (this.selectedActionType != ActionType.ACTION_STOP) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read the action hash directly from the ItemStack's CUSTOM_DATA
|
||||
ItemStack item = this.getItem(slot);
|
||||
if (item.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CustomData customData = item.get(DataComponents.CUSTOM_DATA);
|
||||
if (customData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompoundTag tag = customData.copyTag();
|
||||
if (tag.contains("action_hash")) {
|
||||
return tag.getString("action_hash").get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ActionType at a specific slot, accounting for pagination offset.
|
||||
* Returns null if the slot is not a valid content slot or the index is out of bounds.
|
||||
*/
|
||||
@Nullable
|
||||
public ActionType getActionTypeAtSlot(int slot) {
|
||||
if (!this.isSelectingActionType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int contentIndex = -1;
|
||||
for (int i = 0; i < CONTENT_SLOTS.length; i++) {
|
||||
if (CONTENT_SLOTS[i] == slot) {
|
||||
contentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contentIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ActionType[] actionTypes = ActionType.values();
|
||||
int actualIndex = this.currentPage * CONTENT_SLOTS.length + contentIndex;
|
||||
if (actualIndex >= 0 && actualIndex < actionTypes.length) {
|
||||
return actionTypes[actualIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected ActionType
|
||||
*/
|
||||
@Nullable
|
||||
public ActionType getSelectedActionType() {
|
||||
return this.selectedActionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're in action type selection mode
|
||||
*/
|
||||
public boolean isSelectingActionType() {
|
||||
return this.isSelectingActionType;
|
||||
}
|
||||
|
||||
public boolean canNavigateBack() {
|
||||
return !this.navigationStack.isEmpty() || (!this.isSelectingActionType && this.selectedActionType != null);
|
||||
}
|
||||
|
||||
public boolean canNavigateHome() {
|
||||
return !this.isSelectingActionType && (this.currentNode != null || this.selectedActionType != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the command builder can be executed.
|
||||
* Returns true if there is a current node and it is confirmable.
|
||||
*/
|
||||
public boolean canExecuteCommandBuilder() {
|
||||
return this.currentNode != null && this.currentNode.isConfirmable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GuiNode getCurrentNode() {
|
||||
return this.currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current parameter count (number of selected command nodes).
|
||||
* This equals the navigation stack size plus 1 if currentNode is not null.
|
||||
*/
|
||||
public int getCurrentParameterCount() {
|
||||
return this.navigationStack.size() + (this.currentNode != null ? 1 : 0);
|
||||
}
|
||||
|
||||
public CraftBot getBot() {
|
||||
return this.bot;
|
||||
}
|
||||
|
||||
public CraftPlayer getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NotNull Player player) {
|
||||
return this.player.getHandle() == player && this.bot.isValid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package fun.bm.lophine.bot;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.bot.action.gui.ActionType;
|
||||
import fun.bm.lophine.bot.action.gui.GuiNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
|
||||
public class BotActionGuiMenu extends AbstractContainerMenu {
|
||||
private final BotActionGuiContainer container;
|
||||
private final CraftBot bot;
|
||||
private final CraftPlayer player;
|
||||
private CraftInventoryView view = null;
|
||||
|
||||
public BotActionGuiMenu(int containerId, Inventory inventory, BotActionGuiContainer container) {
|
||||
super(MenuType.GENERIC_9x6, containerId);
|
||||
this.container = container;
|
||||
this.bot = container.getBot();
|
||||
this.player = container.getPlayer();
|
||||
|
||||
for (int row = 0; row < 6; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(container, col + row * 9, 8 + col * 18, 18 + row * 18) {
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col + row * 9 + 9, 8 + col * 18, 140 + row * 18));
|
||||
}
|
||||
}
|
||||
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col, 8 + col * 18, 198));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.InventoryView getBukkitView() {
|
||||
if (this.view == null) {
|
||||
CraftInventory inventory = new CraftInventory(this.container);
|
||||
this.view = new CraftInventoryView(
|
||||
this.player,
|
||||
inventory,
|
||||
this
|
||||
);
|
||||
}
|
||||
return this.view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NotNull Player player) {
|
||||
return this.container.stillValid(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int slotIndex, int buttonNum, ContainerInput containerInput, Player player) {
|
||||
if (slotIndex >= 0 && slotIndex < 54) {
|
||||
if (this.container.isBackButtonSlot(slotIndex)) {
|
||||
this.container.navigateBack();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.container.isHomeButtonSlot(slotIndex)) {
|
||||
this.container.navigateHome();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle pagination buttons
|
||||
if (this.container.isPrevPageSlot(slotIndex)) {
|
||||
this.container.prevPage();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
if (this.container.isNextPageSlot(slotIndex)) {
|
||||
this.container.nextPage();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle command builder click
|
||||
if (this.container.isCommandBuilderSlot(slotIndex) && this.container.canExecuteCommandBuilder()) {
|
||||
this.executeCommandBuilder(player);
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle ActionType selection (with pagination offset)
|
||||
if (this.container.isSelectingActionType()) {
|
||||
ActionType clickedType = this.container.getActionTypeAtSlot(slotIndex);
|
||||
if (clickedType != null) {
|
||||
this.container.selectActionType(clickedType);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle action stop click (when STOP action type is selected)
|
||||
if (this.container.getSelectedActionType() == ActionType.ACTION_STOP) {
|
||||
String actionHash = this.container.getActionHashAtSlot(slotIndex);
|
||||
if (actionHash != null) {
|
||||
this.stopActionByHash(actionHash, player);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GuiNode node = this.container.getGuiNodeAtSlot(slotIndex);
|
||||
if (node != null) {
|
||||
this.handleNodeClick(node, player);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
super.clicked(slotIndex, buttonNum, containerInput, player);
|
||||
}
|
||||
|
||||
private void handleNodeClick(GuiNode node, Player player) {
|
||||
if (node instanceof GuiRootNode rootNode) {
|
||||
// Get the selected action type
|
||||
ActionType actionType = this.container.getSelectedActionType();
|
||||
if (actionType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxAllowedParameters = actionType.getMaxAllowedParameters();
|
||||
|
||||
// If the node has children, check parameter limit before navigating
|
||||
if (!rootNode.getChildren().isEmpty()) {
|
||||
// Calculate current parameter count (navigation stack size + 1 for current node if exists)
|
||||
int currentParamCount = this.container.getCurrentParameterCount();
|
||||
|
||||
// If adding this node would exceed the limit, execute command instead of navigating
|
||||
if (currentParamCount + 1 > maxAllowedParameters) {
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType == ActionType.ACTION_START && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
} else {
|
||||
this.container.navigateToChild(rootNode);
|
||||
}
|
||||
} else {
|
||||
// Execute command with t`he selected action type
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType == ActionType.ACTION_START && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeCommand(GuiRootNode node, ActionType actionType, Player player) {
|
||||
try {
|
||||
String actionPrefix = actionType.getCommandActionPrefix();
|
||||
String actionSuffix = actionType.getCommandActionSuffix();
|
||||
if (!actionSuffix.isEmpty()) {
|
||||
actionSuffix = actionSuffix + " ";
|
||||
}
|
||||
|
||||
String extra = actionPrefix + " " + bot.getName() + " " + actionSuffix;
|
||||
String command = node.buildCommand(extra);
|
||||
|
||||
// Apply parameter limit based on ActionType
|
||||
if (actionType.getMaxAllowedParameters() == 0) {
|
||||
// For actions like STOP that don't allow parameters, trim to just "action botName"
|
||||
command = actionPrefix + " " + bot.getName();
|
||||
}
|
||||
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
MinecraftServer.getServer().getCommands().performPrefixedCommand(
|
||||
serverPlayer.createCommandSourceStack(),
|
||||
command
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Error executing command: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command from the command builder (book item).
|
||||
* Uses the current node and selected action type.
|
||||
*/
|
||||
private void executeCommandBuilder(Player player) {
|
||||
GuiNode currentNode = this.container.getCurrentNode();
|
||||
ActionType actionType = this.container.getSelectedActionType();
|
||||
|
||||
if (currentNode instanceof GuiRootNode rootNode && actionType != null) {
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType != ActionType.ACTION_STOP && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a bot action by its hash (UUID string)
|
||||
*/
|
||||
private void stopActionByHash(String actionHash, Player player) {
|
||||
try {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
String command = getStopActionCommand(actionHash);
|
||||
MinecraftServer.getServer().getCommands().performPrefixedCommand(
|
||||
serverPlayer.createCommandSourceStack(),
|
||||
command
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Error stopping action with hash {}: ", actionHash, e);
|
||||
}
|
||||
// Always refresh to show updated action list after stop attempt
|
||||
this.container.showCurrentBotActions();
|
||||
}
|
||||
|
||||
private String getStopActionCommand(String actionHash) throws UnexpectedException {
|
||||
boolean botCommand = FakeplayerConfig.enable;
|
||||
boolean playerCommand = FakePlayerCompatConfig.commandPlayer;
|
||||
String command;
|
||||
if (botCommand) {
|
||||
command = "bot ";
|
||||
} else if (playerCommand) {
|
||||
command = "player ";
|
||||
} else {
|
||||
throw new UnexpectedException("Unable to build String from commandNode.");
|
||||
}
|
||||
command = command + "action " + this.bot.getName() + " stop " + actionHash;
|
||||
return command;
|
||||
}
|
||||
|
||||
private void refreshSlots() {
|
||||
for (int i = 0; i < 54; i++) {
|
||||
Slot slot = this.slots.get(i);
|
||||
ItemStack item = this.container.getItem(i);
|
||||
slot.set(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public BotActionGuiContainer getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum ActionType {
|
||||
ACTION_START("Start", "Start a action", Items.DYE.lime(), "action", "start"),
|
||||
ACTION_STOP("Stop", "Stop a action or see this bot scheduled tasks", Items.DYE.red(), "action", "stop", 0);
|
||||
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
private final Item item;
|
||||
private final String commandActionPrefix;
|
||||
private final String commandActionSuffix;
|
||||
private final int maxAllowedParameters;
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix) {
|
||||
this(displayName, description, item, commandActionPrefix, "", Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix, String commandActionSuffix) {
|
||||
this(displayName, description, item, commandActionPrefix, commandActionSuffix, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix, String commandActionSuffix, int maxAllowedParameters) {
|
||||
this.displayName = displayName;
|
||||
this.description = description;
|
||||
this.item = item;
|
||||
this.commandActionPrefix = commandActionPrefix;
|
||||
this.commandActionSuffix = commandActionSuffix;
|
||||
this.maxAllowedParameters = maxAllowedParameters;
|
||||
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public String getCommandActionPrefix() {
|
||||
return this.commandActionPrefix;
|
||||
}
|
||||
|
||||
public String getCommandActionSuffix() {
|
||||
return commandActionSuffix;
|
||||
}
|
||||
|
||||
public GuiNode toConfirmNode(GuiRootNode targetNode) {
|
||||
return new ActionConfirmNode(this.displayName, this.description, this.item, targetNode, this.commandActionPrefix, this.maxAllowedParameters);
|
||||
}
|
||||
|
||||
public int getMaxAllowedParameters() {
|
||||
return maxAllowedParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* A special node used in the action type confirmation UI.
|
||||
* It holds a reference to the target GuiRootNode and the selected action string.
|
||||
*/
|
||||
public static class ActionConfirmNode extends GuiNode {
|
||||
private final GuiRootNode targetNode;
|
||||
private final String action;
|
||||
private final int maxAllowedParameters;
|
||||
|
||||
public ActionConfirmNode(String name, String description, Item item, GuiRootNode targetNode, String action, int maxAllowedParameters) {
|
||||
super(name, description, item);
|
||||
this.targetNode = targetNode;
|
||||
this.action = action;
|
||||
this.maxAllowedParameters = maxAllowedParameters;
|
||||
}
|
||||
|
||||
public GuiRootNode getTargetNode() {
|
||||
return this.targetNode;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public int getMaxAllowedParameters() {
|
||||
return this.maxAllowedParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
ItemStack itemStack = super.getItemStack();
|
||||
|
||||
// Add parameter limit info to lore
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
if (this.description != null && !this.description.isEmpty()) {
|
||||
loreLines.add(Component.literal(this.description));
|
||||
}
|
||||
|
||||
if (!loreLines.isEmpty()) {
|
||||
itemStack.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GuiNode {
|
||||
protected final String name;
|
||||
protected final String description;
|
||||
protected final Item item;
|
||||
protected final boolean confirmable;
|
||||
|
||||
private static final Item defaultItem = Items.PAPER;
|
||||
|
||||
public GuiNode(String name, String description, Item item, boolean confirmable) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.item = item;
|
||||
this.confirmable = confirmable;
|
||||
}
|
||||
|
||||
public GuiNode(String name, String description, Item item) {
|
||||
this(name, description, item, true);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public boolean isConfirmable() {
|
||||
return this.confirmable;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack() {
|
||||
ItemStack itemStack = new ItemStack(this.item == null ? defaultItem : this.item).copy();
|
||||
itemStack.set(DataComponents.CUSTOM_NAME, Component.literal(this.name));
|
||||
if (this.description != null && !this.description.isEmpty()) {
|
||||
ItemLore lore = new ItemLore(List.of(Component.literal(this.description)));
|
||||
itemStack.set(DataComponents.LORE, lore);
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GuiRootNode extends GuiNode {
|
||||
protected final Set<GuiNode> children;
|
||||
protected final String commandNode;
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, String commandNode) {
|
||||
this(name, description, item, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, String commandNode, boolean confirmable) {
|
||||
this(name, description, item, new LinkedHashSet<>(), commandNode, confirmable);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, Set<GuiNode> children, String commandNode) {
|
||||
this(name, description, item, children, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, Set<GuiNode> children, String commandNode, boolean confirmable) {
|
||||
super(name, description, item, confirmable);
|
||||
this.children = children;
|
||||
this.commandNode = commandNode;
|
||||
}
|
||||
|
||||
public final void child(GuiNode... node) {
|
||||
this.children.addAll(List.of(node));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void child(Supplier<? extends GuiNode>... node) {
|
||||
this.children.addAll(Stream.of(node).map(Supplier::get).toList());
|
||||
}
|
||||
|
||||
public Set<GuiNode> getChildren() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
public Set<GuiNode> getAllFurthestChildren() {
|
||||
if (this.children.isEmpty()) {
|
||||
Set<GuiNode> result = new LinkedHashSet<>();
|
||||
result.add(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<GuiNode> furthestChildren = new LinkedHashSet<>();
|
||||
for (GuiNode child : this.children) {
|
||||
furthestChildren.addAll(((GuiRootNode) child).getAllFurthestChildren());
|
||||
}
|
||||
return furthestChildren;
|
||||
}
|
||||
|
||||
public String getCommandNode() {
|
||||
return this.commandNode;
|
||||
}
|
||||
|
||||
public String buildCommand(String extra) throws UnexpectedException {
|
||||
boolean botCommand = FakeplayerConfig.enable;
|
||||
boolean playerCommand = FakePlayerCompatConfig.commandPlayer;
|
||||
if (botCommand) {
|
||||
return "bot " + extra + this.getCommandNode();
|
||||
}
|
||||
if (playerCommand) {
|
||||
return "player " + extra + this.getCommandNode();
|
||||
}
|
||||
throw new UnexpectedException("Unable to build String from commandNode.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GuiSubNode extends GuiRootNode {
|
||||
protected final GuiNode parent;
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, Set<GuiNode> children, String commandNode) {
|
||||
this(name, description, item, parent, children, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, Set<GuiNode> children, String commandNode, boolean confirmable) {
|
||||
super(name, description, item, children, commandNode, confirmable);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, String commandNode) {
|
||||
this(name, description, item, parent, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, String commandNode, boolean confirmable) {
|
||||
this(name, description, item, parent, new LinkedHashSet<>(), commandNode, confirmable);
|
||||
}
|
||||
|
||||
public GuiNode getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildCommand(String extra) throws UnexpectedException {
|
||||
return ((GuiRootNode) parent).buildCommand(extra) + " " + getCommandNode();
|
||||
}
|
||||
}
|
||||
+5
@@ -40,6 +40,11 @@ public class FakeplayerConfig implements IConfigModule {
|
||||
Regeneration amount for fakeplayers""")
|
||||
public static double regenAmount = 0.0;
|
||||
|
||||
@ConfigInfo(name = "open-action-gui", comments = """
|
||||
Allow opening fakeplayer action gui,
|
||||
need sneak to open if you enabled inventory open gui""")
|
||||
public static boolean canOpenActionGui = false;
|
||||
|
||||
@ConfigInfo(name = "use-action", comments = """
|
||||
Allow fakeplayers to use actions""")
|
||||
public static boolean canUseAction = true;
|
||||
|
||||
Reference in New Issue
Block a user