commit f57af319690f49d2a4544a643c1dd4d6b1ab79ac
Author: YuTian <2953516620@qq.com>
Date: Sat Jul 27 17:08:05 2024 +0800
1.0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3a27527
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/.idea/
+/target/
+/ElementOriginLib.iml
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..409f972
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,140 @@
+
+
+ 4.0.0
+
+ com.io.yutian.elementoriginlib
+ ElementOriginLib
+ 1.0-SNAPSHOT
+ jar
+
+ ElementOriginLib
+
+
+ 17
+ UTF-8
+
+
+
+ clean package
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+
+ ${java.version}
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.3
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+ papermc-repo
+ https://repo.papermc.io/repository/maven-public/
+
+
+ sonatype
+ https://oss.sonatype.org/content/groups/public/
+
+
+ public
+ https://repo.aurora-pixels.com/repository/public/
+
+
+ public-rpg
+ https://repo.aurora-pixels.com/repository/public-rpg/
+
+
+ spigotmc-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+ rapture-snapshots
+ https://repo.rapture.pw/repository/maven-snapshots/
+
+
+
+
+
+ io.papermc.paper
+ paper-api
+ 1.20.1-R0.1-SNAPSHOT
+ provided
+
+
+ org.spigotmc
+ spigot
+ 1.20.1
+ nms
+ provided
+
+
+ com.mojang
+ authlib
+ 3.3.39
+ provided
+
+
+ com.mojang
+ brigadier
+ 1.0.18
+ provided
+
+
+ com.mojang
+ datafixerupper
+ 4.1.27
+ provided
+
+
+ org.apache.commons
+ commons-compress
+ 1.26.2
+
+
+ com.github.luben
+ zstd-jni
+ 1.5.6-3
+
+
+ com.zaxxer
+ HikariCP
+ 4.0.3
+
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.46.0.0
+
+
+
+ redis.clients
+ jedis
+ 5.1.3
+
+
+
diff --git a/src/main/java/com/io/yutian/elementoriginlib/ElementOriginLib.java b/src/main/java/com/io/yutian/elementoriginlib/ElementOriginLib.java
new file mode 100644
index 0000000..b970359
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/ElementOriginLib.java
@@ -0,0 +1,21 @@
+package com.io.yutian.elementoriginlib;
+
+import org.bukkit.plugin.java.JavaPlugin;
+
+public final class ElementOriginLib extends JavaPlugin {
+
+ private static ElementOriginLib instance;
+
+ @Override
+ public void onEnable() {
+ }
+
+ @Override
+ public void onDisable() {
+ }
+
+ public static ElementOriginLib inst() {
+ return instance;
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/Command.java b/src/main/java/com/io/yutian/elementoriginlib/command/Command.java
new file mode 100644
index 0000000..6c6972e
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/Command.java
@@ -0,0 +1,8 @@
+package com.io.yutian.elementoriginlib.command;
+
+@FunctionalInterface
+public interface Command {
+
+ void run(S context);
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/CommandContext.java b/src/main/java/com/io/yutian/elementoriginlib/command/CommandContext.java
new file mode 100644
index 0000000..f2d2f05
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/CommandContext.java
@@ -0,0 +1,38 @@
+package com.io.yutian.elementoriginlib.command;
+
+import com.io.yutian.elementoriginlib.command.argument.ArgumentValue;
+import org.bukkit.command.CommandSender;
+
+import java.util.Map;
+
+public class CommandContext {
+
+ private String command;
+ private String label;
+ private CommandSender sender;
+ private Map argumentsValues;
+
+ public CommandContext(String command, String label, CommandSender sender, Map argumentsValues) {
+ this.command = command;
+ this.label = label;
+ this.sender = sender;
+ this.argumentsValues = argumentsValues;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public CommandSender getSender() {
+ return sender;
+ }
+
+ public ArgumentValue getArgumentsValue(String key) {
+ return argumentsValues.getOrDefault(key, new ArgumentValue(null));
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/CommandNode.java b/src/main/java/com/io/yutian/elementoriginlib/command/CommandNode.java
new file mode 100644
index 0000000..f5d2086
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/CommandNode.java
@@ -0,0 +1,92 @@
+package com.io.yutian.elementoriginlib.command;
+
+import com.io.yutian.elementoriginlib.command.argument.Argument;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class CommandNode {
+
+ private String name;
+
+ private List childrens = new ArrayList<>();
+ private List arguments = new ArrayList<>();
+
+ private List alias = new ArrayList<>();
+
+ private Predicate commandSenderPredicate = (commandSender -> true);
+ private Command command;
+
+ public CommandNode(String name) {
+ this.name = name;
+ }
+
+ public CommandNode(String name, String[] alias) {
+ this.name = name;
+ this.alias = Arrays.asList(alias);
+ }
+
+ public CommandNode(String name, List alias) {
+ this.name = name;
+ this.alias = alias;
+ }
+
+ public CommandNode permission(Predicate commandSenderPredicate) {
+ this.commandSenderPredicate = commandSenderPredicate;
+ return this;
+ }
+
+ public List getAlias() {
+ return alias;
+ }
+
+ public CommandNode setAlias(List alias) {
+ this.alias = alias;
+ return this;
+ }
+
+ public CommandNode addAilas(String alias) {
+ this.alias.add(alias);
+ return this;
+ }
+
+ public CommandNode addArgument(Argument argument) {
+ arguments.add(argument);
+ return this;
+ }
+
+ public CommandNode addChildren(CommandNode commandNode) {
+ this.childrens.add(commandNode);
+ return this;
+ }
+
+ public List getChildrens() {
+ return childrens;
+ }
+
+ public List getArguments() {
+ return arguments;
+ }
+
+ @Deprecated
+ public CommandNode executes(Command command) {
+ this.command = command;
+ return this;
+ }
+
+ public Command getCommand() {
+ return command;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static CommandNode node(String name) {
+ return new CommandNode(name);
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/IAlias.java b/src/main/java/com/io/yutian/elementoriginlib/command/IAlias.java
new file mode 100644
index 0000000..4f8fe79
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/IAlias.java
@@ -0,0 +1,7 @@
+package com.io.yutian.elementoriginlib.command;
+
+public interface IAlias {
+
+ String[] getAlias();
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/ICommand.java b/src/main/java/com/io/yutian/elementoriginlib/command/ICommand.java
new file mode 100644
index 0000000..e5e914c
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/ICommand.java
@@ -0,0 +1,67 @@
+package com.io.yutian.elementoriginlib.command;
+
+import com.io.yutian.elementoriginlib.command.argument.Argument;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ICommand {
+
+ private String name;
+ private String description;
+
+ private List commandNodes = new ArrayList<>();
+ private List arguments = new ArrayList<>();
+
+ public ICommand(String name) {
+ this(name, null);
+ }
+
+ public ICommand(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public void executes(CommandContext commandContext) {
+ }
+
+ public boolean emptyExecutes(CommandSender commandSender) {
+ return false;
+ }
+
+ public boolean hasPermission(CommandSender sender) {
+ return sender.isOp() || sender.hasPermission(getPermissionPrefix()+"."+name);
+ }
+
+ public String getPermissionPrefix() {
+ return "command."+name;
+ }
+
+ public ICommand addArgument(Argument argument) {
+ arguments.add(argument);
+ return this;
+ }
+
+ public ICommand addCommandNode(CommandNode commandNode) {
+ this.commandNodes.add(commandNode);
+ return this;
+ }
+
+ public List getCommandNodes() {
+ return commandNodes;
+ }
+
+ public List getArguments() {
+ return arguments;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/ICommandManager.java b/src/main/java/com/io/yutian/elementoriginlib/command/ICommandManager.java
new file mode 100644
index 0000000..9b7c580
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/ICommandManager.java
@@ -0,0 +1,11 @@
+package com.io.yutian.elementoriginlib.command;
+
+import java.util.List;
+
+public interface ICommandManager {
+
+ String getName();
+
+ List getCommands();
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/ITabCompleter.java b/src/main/java/com/io/yutian/elementoriginlib/command/ITabCompleter.java
new file mode 100644
index 0000000..41fb3d0
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/ITabCompleter.java
@@ -0,0 +1,29 @@
+package com.io.yutian.elementoriginlib.command;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public interface ITabCompleter {
+
+ List onTabComplete(CommandSender commandSender, String[] args, int index, String lastArg);
+
+ static List getPlayerList(String arg) {
+ List list = new ArrayList<>();
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ String name = player.getName();
+ if (arg != null && !arg.trim().equalsIgnoreCase("")) {
+ if (name.toLowerCase().startsWith(arg.toLowerCase())) {
+ list.add(name);
+ }
+ } else {
+ list.add(name);
+ }
+ }
+ return list;
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandHandler.java b/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandHandler.java
new file mode 100644
index 0000000..3a90352
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandHandler.java
@@ -0,0 +1,295 @@
+package com.io.yutian.elementoriginlib.command;
+
+import com.io.yutian.elementoriginlib.command.argument.Argument;
+import com.io.yutian.elementoriginlib.command.argument.ArgumentValue;
+import com.io.yutian.elementoriginlib.command.handler.CommandHandler;
+import com.io.yutian.elementoriginlib.lang.Lang;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+public class SimpleCommandHandler extends Command {
+
+ private ICommand iCommand;
+
+ public SimpleCommandHandler(String name, ICommand iCommand) {
+ super(name);
+ this.iCommand = iCommand;
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+ executes(sender, commandLabel, args);
+ return true;
+ }
+
+ public void executes(CommandSender sender, String commandLabel, String[] args) {
+ if (!iCommand.hasPermission(sender)) {
+ sender.sendMessage(Lang.get("command-no-permission"));
+ return;
+ }
+ List commandNodes = iCommand.getCommandNodes();
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ stringBuilder.append(args[i]);
+ if (i < args.length - 1) {
+ stringBuilder.append(" ");
+ }
+ }
+ String commandString = stringBuilder.toString();
+ if (commandNodes.size() == 0) {
+ Map map = new HashMap<>();
+ if (iCommand.getArguments().size() > 0) {
+ int argSize = args.length;
+ List arguments = iCommand.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = l;
+ if (index >= args.length) {
+ break;
+ }
+ String arg = args[index];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-unknown-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, arguments, args, -1);
+ }
+ iCommand.executes(new CommandContext(commandString, commandLabel, sender, map));
+ return;
+ }
+
+ int nodeSize = args.length;
+
+ if (commandNodes.size() > 0 && nodeSize == 0) {
+ if (!iCommand.emptyExecutes(sender)) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ }
+ return;
+ }
+
+ String mainNode = args[0];
+
+ Stream nodeStream = commandNodes.stream().filter((n) -> {
+ return n.getName().equalsIgnoreCase(mainNode) || n.getAlias().contains(mainNode);
+ });
+ Optional nodeOptional = nodeStream.findFirst();
+ if (!nodeOptional.isPresent()) {
+ sender.sendMessage(Lang.get("command-unknown-arg", 1, mainNode));
+ return;
+ }
+ CommandNode node = nodeOptional.get();
+
+ if (node.getChildrens().size() > 0) {
+ checkClidren(commandString, commandLabel, sender, 0, args, node);
+ } else {
+ if (node.getCommand() != null) {
+ Map map = new HashMap<>();
+ if (node.getArguments().size() > 0) {
+ int argSize = args.length - 1;
+ List arguments = node.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = l + 1;
+ if (index >= args.length) {
+ break;
+ }
+ String arg = args[index];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-error-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, node.getArguments(), args, 0);
+ }
+ node.getCommand().run(new CommandContext(commandString, commandLabel, sender, map));
+ } else {
+ sender.sendMessage(Lang.get("command-unknown-arg", 2, mainNode));
+ return;
+ }
+ }
+
+ }
+
+ private void checkClidren(String commandString, String commandLabel, CommandSender sender, int i, String[] args, CommandNode node) {
+ i++;
+ if (i >= args.length) {
+ if (node.getCommand() == null) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ } else {
+ node.getCommand().run(new CommandContext(commandString, commandLabel, sender, new HashMap<>()));
+ }
+ return;
+ }
+ String s = args[i];
+ Stream nodeStream = node.getChildrens().stream().filter((n) -> {
+ return n.getName().equalsIgnoreCase(s) || n.getAlias().contains(s);
+ });
+ Optional nodeOptional = nodeStream.findFirst();
+ if (!nodeOptional.isPresent()) {
+ sender.sendMessage(Lang.get("command-unknown-arg", i+1, s));
+ return;
+ }
+ CommandNode node1 = nodeOptional.get();
+
+ if (node1.getChildrens().size() > 0) {
+ checkClidren(commandString, commandLabel, sender, i, args, node1);
+ } else {
+ if (node1.getCommand() != null) {
+ Map map = new HashMap<>();
+ if (node1.getArguments().size() > 0) {
+ int argSize = args.length - i - 1;
+ List arguments = node1.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = i + l + 1;
+ if (index >= args.length) {
+ break;
+ }
+ String arg = args[index];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-unknown-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, node1.getArguments(), args, i);
+ }
+ node1.getCommand().run(new CommandContext(commandString, commandLabel, sender, map));
+ } else {
+ sender.sendMessage(Lang.get("command-unknown-arg", i+1, s));
+ return;
+ }
+ }
+
+ }
+
+ private Map parseArgumentValue(CommandSender commandSender, List argumentList, String[] args, int i) {
+ Map map = new HashMap<>();
+ List arguments = argumentList;
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ if (i+1+l >= args.length) {
+ if (a.isOptional()) {
+ map.put(a.getName(), new ArgumentValue(a.getDefaultValue()));
+ }
+ return map;
+ }
+ String arg = args[i+1+l];
+ if (!a.getArgumentsType().test(arg)) {
+ continue;
+ }
+ ArgumentValue argumentValue = new ArgumentValue(a.getArgumentsType().get(arg));
+ map.put(a.getName(), argumentValue);
+ }
+ return map;
+ }
+
+ @Override
+ public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
+ return onTabComplete(sender, alias, args);
+ }
+
+ public List onTabComplete(CommandSender sender, String alias, String[] args) {
+ List list = new ArrayList<>();
+ int index = args.length;
+ String arg = args[index-1];
+ if (!iCommand.hasPermission(sender)) {
+ return list;
+ }
+ if (iCommand instanceof ITabCompleter) {
+ ITabCompleter tabCompleter = (ITabCompleter) iCommand;
+ return tabCompleter.onTabComplete(sender, args, index-1, arg);
+ } else {
+ Map> map = new HashMap<>();
+ if (iCommand.getCommandNodes().size() > 0) {
+ List list1 = new ArrayList<>();
+ for (CommandNode node : iCommand.getCommandNodes()) {
+ list1.add(node.getName());
+ list1.addAll(node.getAlias());
+ if (index >= 2) {
+ if (!node.getName().equalsIgnoreCase(args[0])) {
+ continue;
+ }
+ }
+ if (node.getChildrens().size() > 0) {
+ getTabComplete(node, 1, map);
+ } else if (node.getArguments().size() > 0) {
+ List arguments = node.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(2+l, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(2+l, Arrays.asList("<"+argument.getName()+">"));
+ }
+ }
+ }
+ map.put(1, list1);
+ return CommandHandler.preseSuggest(map.getOrDefault(index, list), arg);
+ } else if (iCommand.getArguments().size() > 0) {
+ List arguments = iCommand.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(0+l+1, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(0+l+1, Arrays.asList("<"+argument.getName()+">"));
+ }
+ return CommandHandler.preseSuggest(map.getOrDefault(index, list), arg);
+ }
+ }
+ return CommandHandler.preseSuggest(list, arg);
+ }
+
+ private void getTabComplete(CommandNode node, int i, Map> map) {
+ i++;
+ List list = map.getOrDefault(i, new ArrayList<>());
+ for (CommandNode c : node.getChildrens()) {
+ list.add(c.getName());
+ if (c.getChildrens().size() > 0) {
+ getTabComplete(c, i, map);
+ } else if (c.getArguments().size() > 0) {
+ List arguments = c.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(i+l+1, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(i+l+1, Arrays.asList("<"+argument.getName()+">"));
+ }
+ }
+ }
+ map.put(i, list);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandManager.java b/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandManager.java
new file mode 100644
index 0000000..6113962
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/SimpleCommandManager.java
@@ -0,0 +1,116 @@
+package com.io.yutian.elementoriginlib.command;
+
+import com.io.yutian.elementoriginlib.command.handler.CommandHandler;
+import com.io.yutian.elementoriginlib.command.list.CommandHelp;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandMap;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SimpleCommandManager implements ICommandManager {
+
+ protected static Map bukkitCommandMap = new HashMap<>();
+ protected static CommandMap commandMap;
+
+ private final Plugin plugin;
+ private final String name;
+
+ @NotNull
+ private List commands;
+
+
+ public SimpleCommandManager(Plugin plugin, String name) {
+ this(plugin, name, new ArrayList<>());
+ }
+
+ public SimpleCommandManager(Plugin plugin, String name, @NotNull List commands) {
+ this.plugin = plugin;
+ this.name = name;
+ this.commands = commands;
+ register(new CommandHelp(this));
+ }
+
+ public void register(@NotNull ICommand command) {
+ if (command == null) {
+ return;
+ }
+ commands.add(command);
+ if (command instanceof IAlias alias) {
+ String[] array = alias.getAlias();
+ for (String s : array) {
+ registerPluginBukkitCommand(plugin, s, command);
+ }
+ }
+ }
+
+ public void unregisterAll() {
+ for (ICommand command : commands) {
+ if (command instanceof IAlias) {
+ unregister(command);
+ }
+ }
+ }
+
+ public static void unregister(ICommand command) {
+ if (!(command instanceof IAlias)) {
+ return;
+ }
+ try {
+ Map map = (Map) commandMap.getClass().getMethod("getKnownCommands").invoke(commandMap);
+ for (String name : ((IAlias) command).getAlias()) {
+ map.remove(name);
+ Command bukkitCommand = bukkitCommandMap.get(name);
+ bukkitCommand.unregister(commandMap);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void registerPluginCommand(@NotNull Plugin plugin, String commandName) {
+ Bukkit.getPluginCommand(commandName).setExecutor(new CommandHandler(plugin, this));
+ }
+
+ protected static void registerPluginBukkitCommand(Plugin plugin, String name, ICommand command) {
+ SimpleCommandHandler simpleCommandHandler = new SimpleCommandHandler(name, command);
+ bukkitCommandMap.put(name, simpleCommandHandler);
+ commandMap.register(plugin.getName(), simpleCommandHandler);
+ }
+
+ public Plugin getPlugin() {
+ return plugin;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @NotNull
+ @Override
+ public List getCommands() {
+ return commands;
+ }
+
+ static {
+ try {
+ Class> c = Bukkit.getServer().getClass();
+ for (Method method : c.getDeclaredMethods()) {
+ if (method.getName().equals("getCommandMap")) {
+ commandMap = (CommandMap) method.invoke(Bukkit.getServer(), new Object[0]);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/Suggest.java b/src/main/java/com/io/yutian/elementoriginlib/command/Suggest.java
new file mode 100644
index 0000000..d4639b1
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/Suggest.java
@@ -0,0 +1,10 @@
+package com.io.yutian.elementoriginlib.command;
+
+import java.util.List;
+
+@FunctionalInterface
+public interface Suggest {
+
+ List getSuggest();
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/Suggests.java b/src/main/java/com/io/yutian/elementoriginlib/command/Suggests.java
new file mode 100644
index 0000000..eb23c09
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/Suggests.java
@@ -0,0 +1,28 @@
+package com.io.yutian.elementoriginlib.command;
+
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Suggests {
+
+ public static final Suggest WORLD_LIST = ()->{
+ List list = new LinkedList<>();
+ for (World world : Bukkit.getWorlds()) {
+ list.add(world.getName());
+ }
+ return list;
+ };
+
+ public static final Suggest PLAYER_LIST = ()->{
+ List list = new LinkedList<>();
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ list.add(player.getName());
+ }
+ return list;
+ };
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/argument/Argument.java b/src/main/java/com/io/yutian/elementoriginlib/command/argument/Argument.java
new file mode 100644
index 0000000..94f0f5f
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/argument/Argument.java
@@ -0,0 +1,56 @@
+package com.io.yutian.elementoriginlib.command.argument;
+
+import com.io.yutian.elementoriginlib.command.Suggest;
+
+public class Argument {
+
+ private String name;
+
+ private ArgumentType argumentsType;
+
+ private Suggest suggest;
+
+ private boolean optional = false;
+ private Object defaultValue = null;
+
+ public Argument(String name, ArgumentType argumentsType) {
+ this.name = name;
+ this.argumentsType = argumentsType;
+ }
+
+ public Argument optional(Object defaultValue) {
+ optional = true;
+ this.defaultValue = defaultValue;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ArgumentType getArgumentsType() {
+ return argumentsType;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
+
+ public Argument suggest(Suggest suggest) {
+ this.suggest = suggest;
+ return this;
+ }
+
+ public Suggest getSuggest() {
+ return suggest;
+ }
+
+ public static Argument argument(String name, ArgumentType type) {
+ return new Argument(name, type);
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentType.java b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentType.java
new file mode 100644
index 0000000..122fa5d
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentType.java
@@ -0,0 +1,31 @@
+package com.io.yutian.elementoriginlib.command.argument;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class ArgumentType {
+
+ private final String name;
+
+ private Predicate predicate;
+ private Function function;
+
+ public ArgumentType(String name, Predicate predicate, Function function) {
+ this.name = name;
+ this.predicate = predicate;
+ this.function = function;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean test(String string) {
+ return predicate.test(string);
+ }
+
+ public T get(String t) {
+ return function.apply(t);
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentTypes.java b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentTypes.java
new file mode 100644
index 0000000..f4fcf3d
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentTypes.java
@@ -0,0 +1,19 @@
+package com.io.yutian.elementoriginlib.command.argument;
+
+import com.io.yutian.elementoriginlib.util.StringUtil;
+
+import java.util.UUID;
+
+public class ArgumentTypes {
+
+ public static final ArgumentType STRING = new ArgumentType<>("string", (s) -> true, (s) -> s);
+
+ public static final ArgumentType INTEGER = new ArgumentType<>("integer", StringUtil::isInt, Integer::parseInt);
+
+ public static final ArgumentType DOUBLE = new ArgumentType<>("double", StringUtil::isDouble, Double::parseDouble);
+
+ public static final ArgumentType UUID = new ArgumentType<>("uuid", StringUtil::isUUID, java.util.UUID::fromString);
+
+ public static final ArgumentType BOOLEAN = new ArgumentType<>("boolean", StringUtil::isBoolean, Boolean::parseBoolean);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentValue.java b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentValue.java
new file mode 100644
index 0000000..74ac912
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/argument/ArgumentValue.java
@@ -0,0 +1,65 @@
+package com.io.yutian.elementoriginlib.command.argument;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class ArgumentValue {
+
+ private static final Map, Class>> PRIMITIVE_TO_WRAPPER = new HashMap<>();
+
+ static {
+ PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class);
+ PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class);
+ PRIMITIVE_TO_WRAPPER.put(short.class, Short.class);
+ PRIMITIVE_TO_WRAPPER.put(char.class, Character.class);
+ PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class);
+ PRIMITIVE_TO_WRAPPER.put(long.class, Long.class);
+ PRIMITIVE_TO_WRAPPER.put(float.class, Float.class);
+ PRIMITIVE_TO_WRAPPER.put(double.class, Double.class);
+ }
+
+ private Object value;
+
+ public ArgumentValue(Object value) {
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public V get(Class clazz) {
+ if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(value.getClass())) {
+ return (V) value;
+ }
+ return null;
+ }
+
+ public String getString() {
+ return (String) value;
+ }
+
+ public int getInt() {
+ return (Integer) value;
+ }
+
+ public double getDouble() {
+ return (Double) value;
+ }
+
+ public boolean getBoolean() {
+ return (Boolean) value;
+ }
+
+ public UUID getUUID() {
+ return (UUID) value;
+ }
+
+ @Override
+ public String toString() {
+ return "ArgumentValue{" +
+ "value=" + value +
+ '}';
+ }
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/handler/CommandHandler.java b/src/main/java/com/io/yutian/elementoriginlib/command/handler/CommandHandler.java
new file mode 100644
index 0000000..fc35a19
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/handler/CommandHandler.java
@@ -0,0 +1,341 @@
+package com.io.yutian.elementoriginlib.command.handler;
+
+import com.io.yutian.elementoriginlib.command.*;
+import com.io.yutian.elementoriginlib.command.argument.Argument;
+import com.io.yutian.elementoriginlib.command.argument.ArgumentValue;
+import com.io.yutian.elementoriginlib.lang.Lang;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.plugin.Plugin;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CommandHandler implements CommandExecutor, TabCompleter {
+
+ private ICommandManager commandManager;
+
+ public CommandHandler(Plugin plugin, ICommandManager commandManager) {
+ this.commandManager = commandManager;
+ }
+
+ public void execute(CommandSender sender, String label, String[] args) {
+ if (args.length == 0) {
+ execute(sender, label, new String[]{"help", "1"});
+ return;
+ }
+ List commands = commandManager.getCommands();
+ String command = args[0];
+ Stream stream = commands.stream().filter((c) -> c.getName().equalsIgnoreCase(command));
+ Optional optional = stream.findFirst();
+ if (!optional.isPresent()) {
+ sender.sendMessage(Lang.get("command-unknown", command));
+ return;
+ }
+ ICommand iCommand = optional.get();
+ if (!iCommand.hasPermission(sender)) {
+ sender.sendMessage(Lang.get("command-no-permission"));
+ return;
+ }
+ List commandNodes = iCommand.getCommandNodes();
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ stringBuilder.append(args[i]);
+ if (i < args.length - 1) {
+ stringBuilder.append(" ");
+ }
+ }
+ String commandString = stringBuilder.toString();
+ if (commandNodes.size() == 0) {
+ Map map = new HashMap<>();
+ if (iCommand.getArguments().size() > 0) {
+ int argSize = args.length - 1;
+ List arguments = iCommand.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = l + 1;
+ if (index >= args.length) {
+ break;
+ }
+ String arg = args[index];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-unknown-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, arguments, args, 0);
+ }
+ iCommand.executes(new CommandContext(commandString, label, sender, map));
+ return;
+ }
+
+ int nodeSize = args.length - 1;
+
+ if (commandNodes.size() > 0 && nodeSize == 0) {
+ if (!iCommand.emptyExecutes(sender)) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ }
+ return;
+ }
+
+ String mainNode = args[1];
+ Stream nodeStream = commandNodes.stream().filter((n) -> n.getName().equalsIgnoreCase(mainNode) || n.getAlias().contains(mainNode));
+ Optional nodeOptional = nodeStream.findFirst();
+ if (!nodeOptional.isPresent()) {
+ sender.sendMessage(Lang.get("command-unknown-arg", 2, mainNode));
+ return;
+ }
+ CommandNode node = nodeOptional.get();
+
+ if (node.getChildrens().size() > 0) {
+ checkClidren(commandString, label, sender, 1, args, node);
+ } else {
+ if (node.getCommand() != null) {
+ Map map = new HashMap<>();
+ if (node.getArguments().size() > 0) {
+ int argSize = args.length - 2;
+ List arguments = node.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = l + 1;
+ if (index >= args.length) {
+ break;
+ }
+ if (index+1 >= args.length) {
+ break;
+ }
+ String arg = args[index+1];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-error-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, node.getArguments(), args, 1);
+ }
+ node.getCommand().run(new CommandContext(commandString, label, sender, map));
+ } else {
+ sender.sendMessage(Lang.get("command-unknown-arg", 3, mainNode));
+ }
+ }
+
+ }
+
+ private Map parseArgumentValue(CommandSender commandSender, List argumentList, String[] args, int i) {
+ Map map = new HashMap<>();
+ List arguments = argumentList;
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ if (i+1+l >= args.length) {
+ if (a.isOptional()) {
+ map.put(a.getName(), new ArgumentValue(a.getDefaultValue()));
+ }
+ return map;
+ }
+ String arg = args[i+1+l];
+ if (!a.getArgumentsType().test(arg)) {
+ continue;
+ }
+ ArgumentValue argumentValue = new ArgumentValue(a.getArgumentsType().get(arg));
+ map.put(a.getName(), argumentValue);
+ }
+ return map;
+ }
+
+ private void checkClidren(String commandString, String label, CommandSender sender, int i, String[] args, CommandNode node) {
+ i++;
+ if (i >= args.length) {
+ if (node.getCommand() == null) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ } else {
+ node.getCommand().run(new CommandContext(commandString, label, sender, new HashMap<>()));
+ }
+ return;
+ }
+ String s = args[i];
+ Stream nodeStream = node.getChildrens().stream().filter((n) -> n.getName().equalsIgnoreCase(s) || n.getAlias().contains(s));
+ Optional nodeOptional = nodeStream.findFirst();
+ if (!nodeOptional.isPresent()) {
+ sender.sendMessage(Lang.get("command-unknown-arg", i+1, s));
+ return;
+ }
+ CommandNode node1 = nodeOptional.get();
+
+ if (node1.getChildrens().size() > 0) {
+ checkClidren(commandString, label, sender, i, args, node1);
+ } else {
+ if (node1.getCommand() != null) {
+ Map map = new HashMap<>();
+ if (node1.getArguments().size() > 0) {
+ int argSize = args.length - i - 1;
+ List arguments = node1.getArguments();
+ int k = 0;
+ if (arguments.get(arguments.size()-1).isOptional()) {
+ k++;
+ }
+ if (argSize < arguments.size()-k) {
+ sender.sendMessage(Lang.get("command-short-arg"));
+ return;
+ }
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument a = arguments.get(l);
+ int index = i + l + 1;
+ if (index >= args.length) {
+ break;
+ }
+ String arg = args[index];
+ if (!a.getArgumentsType().test(arg)) {
+ sender.sendMessage(Lang.get("command-unknown-arg", index+1, arg));
+ return;
+ }
+ }
+ map = parseArgumentValue(sender, node1.getArguments(), args, i);
+ }
+ node1.getCommand().run(new CommandContext(commandString, label, sender, map));
+ } else {
+ sender.sendMessage(Lang.get("command-unknown-arg", i+1, s));
+ }
+ }
+
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ execute(sender, label, args);
+ return true;
+ }
+
+ @Override
+ public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
+ List list = new ArrayList<>();
+ int index = args.length;
+ String arg = args[index-1];
+ List commands = commandManager.getCommands();
+ if (index == 1) {
+ List commandList = commands.stream().filter((c)->c.getName().startsWith(arg)).collect(Collectors.toList());
+ if (commandList.size() > 0) {
+ commandList.forEach(c-> {
+ if (c.hasPermission(sender)) {
+ list.add(c.getName());
+ }
+ });
+ return list;
+ }
+ commandList = commands.stream().filter((c)->c.getName().contains(arg)).collect(Collectors.toList());
+ if (commandList.size() > 0) {
+ commandList.forEach(c-> {
+ if (c.hasPermission(sender)) {
+ list.add(c.getName());
+ }
+ });
+ return list;
+ }
+ } else {
+ Optional iCommandOptional = commands.stream().filter((c)->c.getName().equalsIgnoreCase(args[0])).findFirst();
+ if (!iCommandOptional.isPresent()) {
+ return list;
+ }
+ ICommand iCommand = iCommandOptional.get();
+ if (!iCommand.hasPermission(sender)) {
+ return list;
+ }
+ if (iCommand instanceof ITabCompleter) {
+ ITabCompleter tabCompleter = (ITabCompleter) iCommand;
+ return tabCompleter.onTabComplete(sender, args, index-2, arg);
+ } else {
+ Map> map = new HashMap<>();
+ if (iCommand.getCommandNodes().size() > 0) {
+ List list1 = new ArrayList<>();
+ for (CommandNode node : iCommand.getCommandNodes()) {
+ list1.add(node.getName());
+ list1.addAll(node.getAlias());
+ if (index >= 2) {
+ if (!node.getName().equalsIgnoreCase(args[1])) {
+ continue;
+ }
+ }
+ if (node.getChildrens().size() > 0) {
+ getTabComplete(node, 2, map);
+ } else if (node.getArguments().size() > 0) {
+ List arguments = node.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(2+l+1, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(2+l+1, Arrays.asList("<"+argument.getName()+">"));
+ }
+ }
+ }
+ map.put(2, list1);
+ return preseSuggest(map.getOrDefault(index, list), arg);
+ } else if (iCommand.getArguments().size() > 0) {
+ List arguments = iCommand.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(1+l+1, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(1+l+1, Arrays.asList("<"+argument.getName()+">"));
+ }
+ return preseSuggest(map.getOrDefault(index, list), arg);
+ }
+ }
+ }
+ return preseSuggest(list, arg);
+ }
+
+ public static List preseSuggest(List list, String arg) {
+ List newList = new ArrayList<>();
+ List list1 = list.stream().filter((c)->c.startsWith(arg)||c.toLowerCase().startsWith(arg.toLowerCase())).collect(Collectors.toList());
+ List list2 = list.stream().filter((c)->c.contains(arg)||c.toLowerCase().contains(arg.toLowerCase())).collect(Collectors.toList());
+ List list3 = list.stream().filter((c)->c.equalsIgnoreCase(arg)|| c.equalsIgnoreCase(arg)).collect(Collectors.toList());
+ newList.addAll(list1);
+ newList.addAll(list2);
+ newList.addAll(list3);
+ return newList;
+ }
+
+ private void getTabComplete(CommandNode node, int i, Map> map) {
+ i++;
+ List list = map.getOrDefault(i, new ArrayList<>());
+ for (CommandNode c : node.getChildrens()) {
+ list.add(c.getName());
+ if (c.getChildrens().size() > 0) {
+ getTabComplete(c, i, map);
+ } else if (c.getArguments().size() > 0) {
+ List arguments = c.getArguments();
+ for (int l = 0; l < arguments.size(); l++) {
+ Argument argument = arguments.get(l);
+ if (argument.getSuggest() != null) {
+ map.put(i+l+1, argument.getSuggest().getSuggest());
+ continue;
+ }
+ map.put(i+l+1, Arrays.asList("<"+argument.getName()+">"));
+ }
+ }
+ }
+ map.put(i, list);
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/command/list/CommandHelp.java b/src/main/java/com/io/yutian/elementoriginlib/command/list/CommandHelp.java
new file mode 100644
index 0000000..167897d
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/command/list/CommandHelp.java
@@ -0,0 +1,117 @@
+package com.io.yutian.elementoriginlib.command.list;
+
+import com.io.yutian.elementoriginlib.command.CommandContext;
+import com.io.yutian.elementoriginlib.command.CommandNode;
+import com.io.yutian.elementoriginlib.command.ICommand;
+import com.io.yutian.elementoriginlib.command.ICommandManager;
+import com.io.yutian.elementoriginlib.command.argument.Argument;
+import com.io.yutian.elementoriginlib.command.argument.ArgumentTypes;
+import com.io.yutian.elementoriginlib.lang.Lang;
+import com.io.yutian.elementoriginlib.util.ComponentBuilder;
+import com.io.yutian.elementoriginlib.list.PageList;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CommandHelp extends ICommand {
+
+ private ICommandManager commandManager;
+ @Nullable
+ private String alias;
+
+ public CommandHelp(ICommandManager commandManager) {
+ this(commandManager, null);
+ }
+
+ public CommandHelp(ICommandManager commandManager, String alias) {
+ super("help");
+ this.commandManager = commandManager;
+ this.alias = alias;
+ addArgument(Argument.argument("page", ArgumentTypes.INTEGER).optional(1));
+ }
+
+ @Override
+ public boolean hasPermission(CommandSender sender) {
+ return true;
+ }
+
+ @Override
+ public void executes(CommandContext commandContext) {
+ String commandAlias = alias != null ? alias : commandContext.getLabel();
+ CommandSender sender = commandContext.getSender();
+ int page = commandContext.getArgumentsValue("page").getInt();
+ if (page <= 0) {
+ sender.sendMessage(Lang.get("command.help.page-error"));
+ return;
+ }
+ List commands = commandManager.getCommands();
+ Stream stream = commands.stream().filter((c) -> c.hasPermission(sender));
+ List list = stream.collect(Collectors.toList());
+ PageList pageList = new PageList<>(list, 8);
+ if (page > pageList.size()) {
+ sender.sendMessage(Lang.get("command.help.page-error"));
+ return;
+ }
+ sender.sendMessage(" ");
+ List commandList = pageList.getList(page);
+ sender.sendMessage("§7======[ §e§l"+commandManager.getName()+" §7]======");
+ for (ICommand command : commandList) {
+ StringBuilder stringBuilder = new StringBuilder("§6/"+commandAlias+" "+command.getName());
+ stringBuilder.append("§f");
+ if (command.getCommandNodes().size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" [");
+ int i = 0;
+ for (CommandNode node : command.getCommandNodes()) {
+ sb.append(node.getName());
+ if (i + 1 < command.getCommandNodes().size()) {
+ sb.append("/");
+ }
+ i++;
+ }
+ sb.append("]");
+ stringBuilder.append(sb);
+ } else {
+ for (Argument argument : command.getArguments()) {
+ stringBuilder.append(" ");
+ stringBuilder.append("<"+argument.getName()+">");
+ }
+ }
+ if (command.getDescription() != null) {
+ stringBuilder.append(" ");
+ stringBuilder.append("§7- §f"+command.getDescription());
+ }
+ sender.sendMessage(stringBuilder.toString());
+ }
+ ComponentBuilder componentBuilder = new ComponentBuilder();
+ boolean hasUpPage = page > 1;
+ boolean hasNextPage = page < pageList.size();
+ componentBuilder.add("§7=====");
+ if (hasUpPage) {
+ componentBuilder.add(" §7["+getColor(true)+"◀§7] ", ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/"+commandAlias+" help "+(page-1)), HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("§f上一页")));
+ } else {
+ componentBuilder.add(" §7["+getColor(false)+"◀§7] ");
+ }
+ componentBuilder.add("§7====");
+ componentBuilder.add("(§a"+page+"§f/§e"+pageList.size()+"§7)");
+ componentBuilder.add("§7====");
+ if (hasNextPage) {
+ componentBuilder.add(" §7["+getColor(true)+"▶§7] ", ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/"+commandAlias+" help "+(page+1)), HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("§f下一页")));
+ } else {
+ componentBuilder.add(" §7["+getColor(false)+"▶§7] ");
+ }
+ componentBuilder.add("§7=====");
+ sender.sendMessage(componentBuilder.build());
+ }
+
+ private String getColor(boolean hasPage) {
+ return hasPage ? "§a" : "§c";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/exception/SerializeException.java b/src/main/java/com/io/yutian/elementoriginlib/exception/SerializeException.java
new file mode 100644
index 0000000..b09572b
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/exception/SerializeException.java
@@ -0,0 +1,9 @@
+package com.io.yutian.elementoriginlib.exception;
+
+public class SerializeException extends RuntimeException {
+
+ public SerializeException(Class clazz, Throwable cause) {
+ super(clazz.toString());
+ }
+
+}
diff --git a/src/main/java/com/io/yutian/elementoriginlib/expiringmap/EntryLoader.java b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/EntryLoader.java
new file mode 100644
index 0000000..6903cdd
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/EntryLoader.java
@@ -0,0 +1,17 @@
+package com.io.yutian.elementoriginlib.expiringmap;
+
+/**
+ * Loads entries on demand.
+ *
+ * @param Key type
+ * @param Value type
+ */
+public interface EntryLoader {
+ /**
+ * Called to load a new value for the {@code key} into an expiring map.
+ *
+ * @param key to load a value for
+ * @return new value to load
+ */
+ V load(K key);
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationListener.java b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationListener.java
new file mode 100644
index 0000000..ac1275e
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationListener.java
@@ -0,0 +1,17 @@
+package com.io.yutian.elementoriginlib.expiringmap;
+
+/**
+ * A listener for expired object events.
+ *
+ * @param Key type
+ * @param Value type
+ */
+public interface ExpirationListener {
+ /**
+ * Called when a map entry expires.
+ *
+ * @param key Expired key
+ * @param value Expired value
+ */
+ void expired(K key, V value);
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationPolicy.java b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationPolicy.java
new file mode 100644
index 0000000..24736c9
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpirationPolicy.java
@@ -0,0 +1,11 @@
+package com.io.yutian.elementoriginlib.expiringmap;
+
+/**
+ * Determines how ExpiringMap entries should be expired.
+ */
+public enum ExpirationPolicy {
+ /** Expires entries based on when they were last accessed */
+ ACCESSED,
+ /** Expires entries based on when they were created */
+ CREATED;
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringEntryLoader.java b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringEntryLoader.java
new file mode 100644
index 0000000..aef842f
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringEntryLoader.java
@@ -0,0 +1,17 @@
+package com.io.yutian.elementoriginlib.expiringmap;
+
+/**
+ * Loads entries on demand, with control over each value's expiry duration (i.e. variable expiration).
+ *
+ * @param Key type
+ * @param Value type
+ */
+public interface ExpiringEntryLoader {
+ /**
+ * Called to load a new value for the {@code key} into an expiring map.
+ *
+ * @param key to load a value for
+ * @return contains new value to load along with its expiry duration
+ */
+ ExpiringValue load(K key);
+}
\ No newline at end of file
diff --git a/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringMap.java b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringMap.java
new file mode 100644
index 0000000..475bfa2
--- /dev/null
+++ b/src/main/java/com/io/yutian/elementoriginlib/expiringmap/ExpiringMap.java
@@ -0,0 +1,1422 @@
+package com.io.yutian.elementoriginlib.expiringmap;
+
+import java.lang.ref.WeakReference;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.io.yutian.elementoriginlib.expiringmap.internal.Assert;
+import com.io.yutian.elementoriginlib.expiringmap.internal.NamedThreadFactory;
+
+/**
+ * A thread-safe map that expires entries. Optional features include expiration policies, variable entry expiration,
+ * lazy entry loading, and expiration listeners.
+ *
+ *
+ * Entries are tracked by expiration time and expired by a single thread.
+ *
+ *
+ * Expiration listeners are called synchronously as entries are expired and block write operations to the map until they
+ * completed. Asynchronous expiration listeners are called on a separate thread pool and do not block map operations.
+ *
+ *
+ * When variable expiration is disabled (default), put/remove operations have a time complexity O(1). When
+ * variable expiration is enabled, put/remove operations have time complexity of O(log n).
+ *
+ *