CommandManager.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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.CommandListenerWrapper;
  16. import net.minecraft.commands.ICompletionProvider;
  17. import net.minecraft.commands.synchronization.CompletionProviders;
  18. import net.minecraft.network.protocol.game.PacketPlayOutCommands;
  19. import net.minecraft.server.level.EntityPlayer;
  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. private final static HashSet<String> NO_PERM_COMMANDS = new HashSet<>();
  34. public static void clearCustomNodes() {
  35. CUSTOM_NODES.clear();
  36. }
  37. public static void addIgnored(String command) {
  38. IGNORED_COMMANDS.add(command);
  39. }
  40. public static void clearIgnored() {
  41. IGNORED_COMMANDS.clear();
  42. }
  43. public static void addNoPerm(String command) {
  44. NO_PERM_COMMANDS.add(command);
  45. }
  46. public static void clearNoPerm() {
  47. NO_PERM_COMMANDS.clear();
  48. }
  49. public static void addCustomNode(CommandNode<?> node) {
  50. CUSTOM_NODES.put(node.getName(), node);
  51. }
  52. public static void add(KajetanCommand command) {
  53. COMMANDS.put(command.getName(), command);
  54. for(String alias : command.getAliases()) {
  55. COMMANDS.put(alias, command);
  56. }
  57. }
  58. private static String getCommandName(String rawCommand) {
  59. if(rawCommand.isEmpty()) {
  60. return "";
  61. }
  62. int index = rawCommand.indexOf(' ');
  63. return rawCommand.substring(rawCommand.charAt(0) == '/' ? 1 : 0,
  64. index == -1 ? rawCommand.length() : index).toLowerCase();
  65. }
  66. private static String[] getArguments(String rawCommand) {
  67. int old = rawCommand.indexOf(' ') + 1;
  68. if(old == 0) {
  69. return new String[0];
  70. }
  71. int pos = old;
  72. ArrayList<String> list = new ArrayList<>();
  73. while(pos < rawCommand.length()) {
  74. char c = rawCommand.charAt(pos);
  75. switch(c) {
  76. case ' ':
  77. if(pos - old > 0) {
  78. list.add(rawCommand.substring(old, pos));
  79. }
  80. old = pos + 1;
  81. break;
  82. case '"':
  83. if(pos - old > 0) {
  84. list.add(rawCommand.substring(old, pos));
  85. }
  86. old = pos + 1;
  87. pos = old;
  88. while(pos < rawCommand.length() && rawCommand.charAt(pos) != '"') {
  89. pos++;
  90. }
  91. list.add(rawCommand.substring(old, pos));
  92. old = pos + 1;
  93. break;
  94. }
  95. pos++;
  96. }
  97. if(pos - old > 0) {
  98. list.add(rawCommand.substring(old, pos));
  99. }
  100. return list.toArray(new String[list.size()]);
  101. }
  102. private static boolean checkPerm(CommandSender cs, String perm) {
  103. if(cs.hasPermission(perm)) {
  104. return true;
  105. }
  106. int point = perm.indexOf(".");
  107. if(point != -1) {
  108. String all = perm.substring(0, point) + ".*";
  109. return cs.hasPermission(all);
  110. }
  111. return false;
  112. }
  113. public static boolean execute(CommandSender cs, String rawCommand) {
  114. String commandName = getCommandName(rawCommand);
  115. KajetanCommand command = COMMANDS.get(commandName);
  116. if(command != null) {
  117. if(cs.hasPermission(command.getName())) {
  118. command.execute(cs, getArguments(rawCommand));
  119. return true;
  120. }
  121. ScriptEvents.onMissingPermission(cs, command.getName(), command.getName());
  122. return true;
  123. }
  124. if(hasCustom(commandName)) {
  125. ScriptEvents.onCustomCommand(cs, commandName, getArguments(rawCommand));
  126. return true;
  127. }
  128. Command bCommand = Bukkit.getServer().getCommandMap().getCommand(commandName);
  129. if(bCommand == null) {
  130. ScriptEvents.onMissingCommand(cs, commandName);
  131. return true;
  132. }
  133. String perm = bCommand.getPermission();
  134. if(perm == null || perm.isEmpty()) {
  135. perm = "missing." + bCommand.getName();
  136. }
  137. if(!checkPerm(cs, perm)) {
  138. ScriptEvents.onMissingPermission(cs, commandName, perm);
  139. return true;
  140. }
  141. if(cs instanceof Player && ScriptEvents.onCommand((Player) cs, commandName)) {
  142. return true;
  143. }
  144. return false;
  145. }
  146. @SuppressWarnings({"unchecked", "rawtypes"})
  147. public static void send(Player player) {
  148. EntityPlayer p = NMS.map(player);
  149. Map<CommandNode<CommandListenerWrapper>, CommandNode<ICompletionProvider>> map =
  150. Maps.newIdentityHashMap();
  151. RootCommandNode<CommandListenerWrapper> vanilla =
  152. p.c.vanillaCommandDispatcher.a().getRoot();
  153. RootCommandNode<ICompletionProvider> rootNode = new RootCommandNode<>();
  154. map.put(vanilla, rootNode);
  155. CommandListenerWrapper cs = p.cQ();
  156. commandSourceNodesToSuggestionNodes(true, vanilla, rootNode, cs, map);
  157. //commandSourceNodesToSuggestionNodes(true, p.c.aB.c.a().getRoot(), rootNode, cs, map);
  158. commandSourceNodesToSuggestionNodes(true, p.c.aA().a().getRoot(), rootNode, cs, map);
  159. for(CommandNode node : CUSTOM_NODES.values()) {
  160. commandSourceNodesToSuggestionNodes(node, rootNode, cs, map);
  161. }
  162. p.b.a(new PacketPlayOutCommands(rootNode));
  163. }
  164. private static boolean checkNoPerm(boolean first, CommandNode<CommandListenerWrapper> c,
  165. CommandListenerWrapper source) {
  166. if(!first) {
  167. return true;
  168. }
  169. if(NO_PERM_COMMANDS.contains(c.getName())) {
  170. return source.getBukkitSender().hasPermission("missing." + c.getName());
  171. }
  172. return true;
  173. }
  174. @SuppressWarnings({"unchecked", "rawtypes"})
  175. private static void commandSourceNodesToSuggestionNodes(boolean first,
  176. CommandNode<CommandListenerWrapper> node, CommandNode<ICompletionProvider> suggestion,
  177. CommandListenerWrapper source,
  178. Map<CommandNode<CommandListenerWrapper>, CommandNode<ICompletionProvider>> map) {
  179. for(CommandNode<CommandListenerWrapper> childNode : node.getChildren()) {
  180. if(first && IGNORED_COMMANDS.contains(childNode.getName())) {
  181. continue;
  182. }
  183. if(childNode.canUse(source) && checkNoPerm(first, childNode, source)) {
  184. ArgumentBuilder<ICompletionProvider, ?> arg =
  185. (ArgumentBuilder) childNode.createBuilder();
  186. arg.requires(a -> true);
  187. if(arg.getCommand() != null) {
  188. arg.executes(a -> 0);
  189. }
  190. if(arg instanceof RequiredArgumentBuilder) {
  191. RequiredArgumentBuilder<ICompletionProvider, ?> required =
  192. (RequiredArgumentBuilder) arg;
  193. if(required.getSuggestionsProvider() != null) {
  194. required.suggests(CompletionProviders.b(required.getSuggestionsProvider()));
  195. }
  196. }
  197. if(arg.getRedirect() != null) {
  198. arg.redirect(map.get(arg.getRedirect()));
  199. }
  200. CommandNode<ICompletionProvider> commandNode = arg.build();
  201. map.put(childNode, commandNode);
  202. suggestion.addChild(commandNode);
  203. if(!childNode.getChildren().isEmpty()) {
  204. commandSourceNodesToSuggestionNodes(false, childNode, commandNode, source, map);
  205. }
  206. }
  207. }
  208. }
  209. @SuppressWarnings({"unchecked", "rawtypes"})
  210. private static void commandSourceNodesToSuggestionNodes(
  211. CommandNode<CommandListenerWrapper> node, CommandNode<ICompletionProvider> parentNode,
  212. CommandListenerWrapper cs,
  213. Map<CommandNode<CommandListenerWrapper>, CommandNode<ICompletionProvider>> map) {
  214. if(!node.canUse(cs)) {
  215. return;
  216. }
  217. ArgumentBuilder<ICompletionProvider, ?> arg = (ArgumentBuilder) node.createBuilder();
  218. arg.requires(a -> true);
  219. if(arg.getCommand() != null) {
  220. arg.executes(a -> 0);
  221. }
  222. if(arg instanceof RequiredArgumentBuilder) {
  223. RequiredArgumentBuilder<ICompletionProvider, ?> required =
  224. (RequiredArgumentBuilder) arg;
  225. if(required.getSuggestionsProvider() != null) {
  226. required.suggests(CompletionProviders.b(required.getSuggestionsProvider()));
  227. }
  228. }
  229. if(arg.getRedirect() != null) {
  230. arg.redirect(map.get(arg.getRedirect()));
  231. }
  232. CommandNode<ICompletionProvider> commandNode = arg.build();
  233. map.put(node, commandNode);
  234. parentNode.addChild(commandNode);
  235. for(CommandNode<CommandListenerWrapper> childNode : node.getChildren()) {
  236. commandSourceNodesToSuggestionNodes(childNode, commandNode, cs, map);
  237. }
  238. }
  239. public static void addCustom(String command) {
  240. SNUVI_COMMANDS.add(command);
  241. }
  242. public static void removeCustom(String command) {
  243. SNUVI_COMMANDS.remove(command);
  244. }
  245. public static boolean hasCustom(String command) {
  246. return SNUVI_COMMANDS.contains(command);
  247. }
  248. public static void clearCustom() {
  249. SNUVI_COMMANDS.clear();
  250. }
  251. public static void clearPermissions(Player p) {
  252. for(PermissionAttachmentInfo info : p.getEffectivePermissions()) {
  253. if(info.getAttachment() != null) {
  254. info.getAttachment().remove();
  255. }
  256. }
  257. for(PermissionAttachmentInfo info : p.getEffectivePermissions()) {
  258. if(info.getAttachment() == null) {
  259. p.addAttachment(KajetansPlugin.instance, info.getPermission(), false);
  260. }
  261. }
  262. if(p.getUniqueId().equals(MARVINIUS) || p.getUniqueId().equals(KAJETANJOHANNES)) {
  263. PermissionAttachment perm = p.addAttachment(KajetansPlugin.instance, "script", true);
  264. perm.setPermission("script.debug", true);
  265. perm.setPermission("script.error", true);
  266. }
  267. p.recalculatePermissions();
  268. }
  269. }