CommandManager.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. package me.hammerle.kp.snuviscript;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.HashSet;
  5. import java.util.Map;
  6. import java.util.UUID;
  7. import org.bukkit.Bukkit;
  8. import org.bukkit.command.Command;
  9. import org.bukkit.command.CommandSender;
  10. import org.bukkit.entity.Player;
  11. import org.bukkit.permissions.PermissionAttachment;
  12. import org.bukkit.permissions.PermissionAttachmentInfo;
  13. import me.hammerle.kp.KajetansPlugin;
  14. import me.hammerle.kp.NMS;
  15. import net.minecraft.commands.CommandSourceStack;
  16. import net.minecraft.commands.SharedSuggestionProvider;
  17. import net.minecraft.commands.synchronization.SuggestionProviders;
  18. import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
  19. import net.minecraft.server.level.ServerPlayer;
  20. import com.google.common.collect.Maps;
  21. import com.mojang.brigadier.builder.ArgumentBuilder;
  22. import com.mojang.brigadier.builder.RequiredArgumentBuilder;
  23. import com.mojang.brigadier.tree.CommandNode;
  24. import com.mojang.brigadier.tree.RootCommandNode;
  25. public class CommandManager {
  26. private final static UUID MARVINIUS = UUID.fromString("e41b5335-3c74-46e9-a6c5-dafc6334a477");
  27. private final static UUID KAJETANJOHANNES =
  28. UUID.fromString("51e240f9-ab10-4ea6-8a5d-779319f51257");
  29. private final static HashMap<String, KajetanCommand> COMMANDS = new HashMap<>();
  30. private final static HashSet<String> SNUVI_COMMANDS = new HashSet<>();
  31. private final static HashMap<String, CommandNode<?>> CUSTOM_NODES = new HashMap<>();
  32. private final static HashSet<String> IGNORED_COMMANDS = new HashSet<>();
  33. public static void clearCustomNodes() {
  34. CUSTOM_NODES.clear();
  35. }
  36. public static void addIgnored(String command) {
  37. IGNORED_COMMANDS.add(command);
  38. }
  39. public static void clearIgnored() {
  40. IGNORED_COMMANDS.clear();
  41. }
  42. public static void addCustomNode(CommandNode<?> node) {
  43. CUSTOM_NODES.put(node.getName(), node);
  44. }
  45. public static void add(KajetanCommand command) {
  46. COMMANDS.put(command.getName(), command);
  47. for(String alias : command.getAliases()) {
  48. COMMANDS.put(alias, command);
  49. }
  50. }
  51. private static String getCommandName(String rawCommand) {
  52. if(rawCommand.isEmpty()) {
  53. return "";
  54. }
  55. int index = rawCommand.indexOf(' ');
  56. return rawCommand.substring(rawCommand.charAt(0) == '/' ? 1 : 0,
  57. index == -1 ? rawCommand.length() : index).toLowerCase();
  58. }
  59. private static String[] getArguments(String rawCommand) {
  60. int old = rawCommand.indexOf(' ') + 1;
  61. if(old == 0) {
  62. return new String[0];
  63. }
  64. int pos = old;
  65. ArrayList<String> list = new ArrayList<>();
  66. while(pos < rawCommand.length()) {
  67. char c = rawCommand.charAt(pos);
  68. switch(c) {
  69. case ' ':
  70. if(pos - old > 0) {
  71. list.add(rawCommand.substring(old, pos));
  72. }
  73. old = pos + 1;
  74. break;
  75. case '"':
  76. if(pos - old > 0) {
  77. list.add(rawCommand.substring(old, pos));
  78. }
  79. old = pos + 1;
  80. pos = old;
  81. while(pos < rawCommand.length() && rawCommand.charAt(pos) != '"') {
  82. pos++;
  83. }
  84. list.add(rawCommand.substring(old, pos));
  85. old = pos + 1;
  86. break;
  87. }
  88. pos++;
  89. }
  90. if(pos - old > 0) {
  91. list.add(rawCommand.substring(old, pos));
  92. }
  93. return list.toArray(new String[list.size()]);
  94. }
  95. public static void execute(CommandSender cs, String rawCommand) {
  96. String commandName = getCommandName(rawCommand);
  97. KajetanCommand command = COMMANDS.get(commandName);
  98. if(command != null) {
  99. if(cs.hasPermission(command.getName())) {
  100. command.execute(cs, getArguments(rawCommand));
  101. return;
  102. }
  103. ScriptEvents.onMissingPermission(cs, command.getName());
  104. return;
  105. }
  106. if(hasCustom(commandName)) {
  107. ScriptEvents.onCustomCommand(cs, commandName, getArguments(rawCommand));
  108. return;
  109. }
  110. if(!cs.hasPermission(commandName)) {
  111. ScriptEvents.onMissingPermission(cs, commandName);
  112. return;
  113. } else if(cs instanceof Player && ScriptEvents.onCommand((Player) cs, commandName)) {
  114. return;
  115. }
  116. Command bCommand = Bukkit.getServer().getCommandMap().getCommand(commandName);
  117. if(bCommand == null) {
  118. ScriptEvents.onMissingCommand(cs, commandName);
  119. return;
  120. }
  121. String perm = bCommand.getPermission();
  122. PermissionAttachment pa = cs.addAttachment(KajetansPlugin.instance, perm, true);
  123. try {
  124. bCommand.execute(cs, commandName, getArguments(rawCommand));
  125. } catch(Throwable ex) {
  126. KajetansPlugin.warn(ex.getMessage());
  127. }
  128. cs.removeAttachment(pa);
  129. }
  130. @SuppressWarnings({"unchecked", "rawtypes"})
  131. public static void send(Player player) {
  132. ServerPlayer p = NMS.map(player);
  133. Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map =
  134. Maps.newIdentityHashMap();
  135. RootCommandNode<CommandSourceStack> vanilla =
  136. p.server.vanillaCommandDispatcher.getDispatcher().getRoot();
  137. RootCommandNode<SharedSuggestionProvider> rootNode = new RootCommandNode<>();
  138. map.put(vanilla, rootNode);
  139. CommandSourceStack cs = ((net.minecraft.world.entity.Entity) p).createCommandSourceStack();
  140. commandSourceNodesToSuggestionNodes(true, vanilla, rootNode, cs, map);
  141. for(CommandNode node : CUSTOM_NODES.values()) {
  142. commandSourceNodesToSuggestionNodes(node, rootNode, cs, map);
  143. }
  144. p.connection.send(new ClientboundCommandsPacket(rootNode));
  145. }
  146. @SuppressWarnings({"unchecked", "rawtypes"})
  147. private static void commandSourceNodesToSuggestionNodes(boolean first,
  148. CommandNode<CommandSourceStack> node, CommandNode<SharedSuggestionProvider> suggestion,
  149. CommandSourceStack source,
  150. Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map) {
  151. for(CommandNode<CommandSourceStack> childNode : node.getChildren()) {
  152. if(first && IGNORED_COMMANDS.contains(childNode.getName())) {
  153. continue;
  154. }
  155. if((first && source.getBukkitSender().hasPermission(childNode.getName())
  156. || (!first && childNode.canUse(source)))) {
  157. ArgumentBuilder<SharedSuggestionProvider, ?> arg =
  158. (ArgumentBuilder) childNode.createBuilder();
  159. arg.requires(a -> true);
  160. if(arg.getCommand() != null) {
  161. arg.executes(a -> 0);
  162. }
  163. if(arg instanceof RequiredArgumentBuilder) {
  164. RequiredArgumentBuilder<SharedSuggestionProvider, ?> required =
  165. (RequiredArgumentBuilder) arg;
  166. if(required.getSuggestionsProvider() != null) {
  167. required.suggests(
  168. SuggestionProviders.safelySwap(required.getSuggestionsProvider()));
  169. }
  170. }
  171. if(arg.getRedirect() != null) {
  172. arg.redirect(map.get(arg.getRedirect()));
  173. }
  174. CommandNode<SharedSuggestionProvider> commandNode = arg.build();
  175. map.put(childNode, commandNode);
  176. suggestion.addChild(commandNode);
  177. if(!childNode.getChildren().isEmpty()) {
  178. commandSourceNodesToSuggestionNodes(false, childNode, commandNode, source, map);
  179. }
  180. }
  181. }
  182. }
  183. @SuppressWarnings({"unchecked", "rawtypes"})
  184. private static void commandSourceNodesToSuggestionNodes(
  185. CommandNode<CommandSourceStack> node, CommandNode<SharedSuggestionProvider> parentNode,
  186. CommandSourceStack cs,
  187. Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map) {
  188. if(!node.canUse(cs)) {
  189. return;
  190. }
  191. ArgumentBuilder<SharedSuggestionProvider, ?> arg = (ArgumentBuilder) node.createBuilder();
  192. arg.requires(a -> true);
  193. if(arg.getCommand() != null) {
  194. arg.executes(a -> 0);
  195. }
  196. if(arg instanceof RequiredArgumentBuilder) {
  197. RequiredArgumentBuilder<SharedSuggestionProvider, ?> required =
  198. (RequiredArgumentBuilder) arg;
  199. if(required.getSuggestionsProvider() != null) {
  200. required.suggests(
  201. SuggestionProviders.safelySwap(required.getSuggestionsProvider()));
  202. }
  203. }
  204. if(arg.getRedirect() != null) {
  205. arg.redirect(map.get(arg.getRedirect()));
  206. }
  207. CommandNode<SharedSuggestionProvider> commandNode = arg.build();
  208. map.put(node, commandNode);
  209. parentNode.addChild(commandNode);
  210. for(CommandNode<CommandSourceStack> childNode : node.getChildren()) {
  211. commandSourceNodesToSuggestionNodes(childNode, commandNode, cs, map);
  212. }
  213. }
  214. public static void addCustom(String command) {
  215. SNUVI_COMMANDS.add(command);
  216. }
  217. public static void removeCustom(String command) {
  218. SNUVI_COMMANDS.remove(command);
  219. }
  220. public static boolean hasCustom(String command) {
  221. return SNUVI_COMMANDS.contains(command);
  222. }
  223. public static void clearCustom() {
  224. SNUVI_COMMANDS.clear();
  225. }
  226. public static void clearPermissions(Player p) {
  227. for(PermissionAttachmentInfo info : p.getEffectivePermissions()) {
  228. if(info.getAttachment() != null) {
  229. info.getAttachment().remove();
  230. }
  231. }
  232. for(PermissionAttachmentInfo info : p.getEffectivePermissions()) {
  233. if(info.getAttachment() == null) {
  234. p.addAttachment(KajetansPlugin.instance, info.getPermission(), false);
  235. }
  236. }
  237. if(p.getUniqueId().equals(MARVINIUS) || p.getUniqueId().equals(KAJETANJOHANNES)) {
  238. PermissionAttachment perm = p.addAttachment(KajetansPlugin.instance, "script", true);
  239. perm.setPermission("script.debug", true);
  240. perm.setPermission("script.error", true);
  241. }
  242. p.recalculatePermissions();
  243. }
  244. }