From d8d2a0de6a5301343371779b8a0d82c128537128 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sat, 25 Jun 2016 07:27:36 +0200 Subject: [PATCH 1/8] Added command intercepter tool, used it for //calc --- misc.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/misc.py b/misc.py index 026650e..1cbe8fb 100644 --- a/misc.py +++ b/misc.py @@ -8,8 +8,136 @@ import org.bukkit.inventory.ItemStack as ItemStack import org.bukkit.Bukkit as Bukkit from basecommands import simplecommand, Validate import java.util.Arrays as Arrays +import org.bukkit.command.Command as Command +import java.util.HashMap as HashMap +""" +This section is a little tool to intercept commands. +It can intercept any aliases of the command, which it does by default. + +An intercepter is a function that is run before the command is executed, +which takes the parameters: sender, args +It should return a boolean value to specify whether it should be executed. + +The optional tab completer is a function that takes the following parameters: + original_completions (List), sender, alias, args +and it should return a List. By default it returns the original completions. +""" + +class CommandInterceptions: + + def __init__(self): + raise Exception("Instances of 'CommandInterceptions' are not meant to exist") + + registrations = {} # cmd : (intercepter, tab_completer) + interceptions = {} # original_obj : replacement_obj + cmd_map = None # CustomHashMap + + @staticmethod + def register(plugin_name, command, intercepter, tab_completer = None): + key = (plugin_name + ":" + command if plugin_name else command).lower() + CommandInterceptions.registrations[key] = (intercepter, tab_completer) + if CommandInterceptions.cmd_map.containsKey(key): + CommandInterceptions.add_interception(key, CommandInterceptions.cmd_map.get(key)) + + + class Intercepter(Command): + + def __init__(self, wrapped, intercepter, tab_completer): + try: + Command.__init__(self, wrapped.getName()) + self.setDescription(wrapped.getDescription()) + self.setPermission(wrapped.getPermission()) + self.setUsage(wrapped.getUsage()) + + # I had to dig deep in spigot code to find out what's happening + # But without this snippet, the server shuts itself down because + # commands can't be aliases for themselves (shrug) :) + aliases = wrapped.getAliases() + aliases.remove(wrapped.getLabel()) + self.setAliases(aliases) + self.wrapped = wrapped + self.intercepter = intercepter + self.tab_completer = tab_completer + except: + error(trace()) + + def execute(self, sender, label, args): + if self.intercepter(sender, args): + return self.wrapped.execute(sender, label, args) + return True + + def tabComplete(self, sender, alias, args): + return self.tab_completer(self.wrapped.tabComplete(sender, alias, args), sender, alias, args) + + + @staticmethod + def add_interception(key, intercepted): + try: + info("Adding interception for /" + key) + registration = CommandInterceptions.registrations[key] + tab_completer = registration[1] + if tab_completer is None: + tab_completer = lambda original_completions, sender, alias, args: original_completions + cmd_intercepter = CommandInterceptions.Intercepter(intercepted, registration[0], tab_completer) + CommandInterceptions.interceptions[intercepted] = cmd_intercepter + for entry in CommandInterceptions.cmd_map.entrySet(): + if entry.getValue() is intercepted: + entry.setValue(cmd_intercepter) + except: + error(trace()) + + @staticmethod + def init_interceptions(): + + # The map object in the command map used by spigot is previously already a hashmap, we replace its put() here + class CustomHashMap(HashMap): + + def __init__(self, replaced): + HashMap.__init__(self) + for entry in replaced.entrySet(): + self.put(entry.getKey(), entry.getValue()) + + def java_put(self, key, value): + return HashMap.put(self, key, value) + + def put(self, key, value): + try: + for intercepted in CommandInterceptions.interceptions: + if value is intercepted: + return self.java_put(key, CommandInterceptions.interceptions[intercepted]) + ret = self.java_put(key, value) + if key in CommandInterceptions.registrations: + CommandInterceptions.add_interception(key, value) + return ret + except: + error(trace()) + + try: + map_field = server.getPluginManager().getClass().getDeclaredField("commandMap") + map_field.setAccessible(True) + command_map = map_field.get(server.getPluginManager()) + + commands_field = command_map.getClass().getDeclaredField("knownCommands") + commands_field.setAccessible(True) + CommandInterceptions.cmd_map = CustomHashMap(commands_field.get(command_map)) + commands_field.set(command_map, CommandInterceptions.cmd_map) + except: + error("[Interceptions] Failed to wrap the command map:") + error(trace()) +CommandInterceptions.init_interceptions() + + +def worldedit_calc_intercepter(sender, args): + info("WorldEdit Calc Intercepter WOOHOO") + if not sender.hasPermission("worldedit.calc"): + noperm(sender) + return False + return True + +CommandInterceptions.register("worldedit", "/calc", worldedit_calc_intercepter) + @hook.event("player.PlayerJoinEvent", "monitor") def on_join(event): -- 2.45.2 From 06aff62fad918c050f8c1255fb71dfeb726822f7 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sat, 25 Jun 2016 07:34:11 +0200 Subject: [PATCH 2/8] Removed now unneeded comment from the command intercepter --- misc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misc.py b/misc.py index 1cbe8fb..29910d4 100644 --- a/misc.py +++ b/misc.py @@ -130,7 +130,6 @@ CommandInterceptions.init_interceptions() def worldedit_calc_intercepter(sender, args): - info("WorldEdit Calc Intercepter WOOHOO") if not sender.hasPermission("worldedit.calc"): noperm(sender) return False -- 2.45.2 From 5b118669b649f4aff6118f3e9fc65218460da12f Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sat, 25 Jun 2016 11:47:31 +0200 Subject: [PATCH 3/8] fixed small issue with interceptions --- misc.py | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/misc.py b/misc.py index 29910d4..fa45925 100644 --- a/misc.py +++ b/misc.py @@ -55,7 +55,10 @@ class CommandInterceptions: # But without this snippet, the server shuts itself down because # commands can't be aliases for themselves (shrug) :) aliases = wrapped.getAliases() - aliases.remove(wrapped.getLabel()) + try: + aliases.remove(wrapped.getLabel()) + except: + pass self.setAliases(aliases) self.wrapped = wrapped self.intercepter = intercepter @@ -74,19 +77,15 @@ class CommandInterceptions: @staticmethod def add_interception(key, intercepted): - try: - info("Adding interception for /" + key) - registration = CommandInterceptions.registrations[key] - tab_completer = registration[1] - if tab_completer is None: - tab_completer = lambda original_completions, sender, alias, args: original_completions - cmd_intercepter = CommandInterceptions.Intercepter(intercepted, registration[0], tab_completer) - CommandInterceptions.interceptions[intercepted] = cmd_intercepter - for entry in CommandInterceptions.cmd_map.entrySet(): - if entry.getValue() is intercepted: - entry.setValue(cmd_intercepter) - except: - error(trace()) + registration = CommandInterceptions.registrations[key] + tab_completer = registration[1] + if tab_completer is None: + tab_completer = lambda original_completions, sender, alias, args: original_completions + cmd_intercepter = CommandInterceptions.Intercepter(intercepted, registration[0], tab_completer) + CommandInterceptions.interceptions[intercepted] = cmd_intercepter + for entry in CommandInterceptions.cmd_map.entrySet(): + if entry.getValue() is intercepted: + entry.setValue(cmd_intercepter) @staticmethod def init_interceptions(): @@ -103,16 +102,13 @@ class CommandInterceptions: return HashMap.put(self, key, value) def put(self, key, value): - try: - for intercepted in CommandInterceptions.interceptions: - if value is intercepted: - return self.java_put(key, CommandInterceptions.interceptions[intercepted]) - ret = self.java_put(key, value) - if key in CommandInterceptions.registrations: - CommandInterceptions.add_interception(key, value) - return ret - except: - error(trace()) + for intercepted in CommandInterceptions.interceptions: + if value is intercepted: + return self.java_put(key, CommandInterceptions.interceptions[intercepted]) + ret = self.java_put(key, value) + if key in CommandInterceptions.registrations: + CommandInterceptions.add_interception(key, value) + return ret try: map_field = server.getPluginManager().getClass().getDeclaredField("commandMap") -- 2.45.2 From 80eff984e84fd9b1aa1754bd824fba201ed4c0f4 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sat, 25 Jun 2016 11:48:15 +0200 Subject: [PATCH 4/8] Replaced imbusy interception implementations with the tool from misc --- imbusy.py | 129 +++++++++++++----------------------------------------- main.py | 2 - 2 files changed, 31 insertions(+), 100 deletions(-) diff --git a/imbusy.py b/imbusy.py index a8c5280..2060c89 100644 --- a/imbusy.py +++ b/imbusy.py @@ -209,110 +209,43 @@ def reply(sender): reply_targets[target.getName()] = (sender_name, True) return True +try: -class CommandWrapper(Command): + def msg_interception(sender, args): + return not is_player(sender) or len(args) <= 1 or whisper(sender, args[0]) - def __init__(self, wrapped, checker): - Command.__init__(self, wrapped.getName()) - self.setDescription(wrapped.getDescription()) - self.setPermission(wrapped.getPermission()) - self.setUsage(wrapped.getUsage()) - self.setAliases(wrapped.getAliases()) - self.wrapped = wrapped - self.checker = checker + def reply_interception(sender, args): + return not is_player(sender) or len(args) == 0 or reply(sender) - def execute(self, sender, label, args): - try: - if not is_player(sender) or self.checker(sender, args): - return self.wrapped.execute(sender, label, args) - except: - error(trace()) + def tpa_interception(sender, args): + if len(args) == 0 or not is_player(sender): + return True + target = server.getPlayer(args[0]) + if target is not None and not can_send(sender, target): + msg(sender, "&c[&fBUSY&c] %s&r is busy!" % target.getDisplayName()) + return False return True - def tabComplete(self, sender, alias, args): - return self.wrapped.tabComplete(sender, alias, args) + def tpahere_interception(sender, args): + return tpa_command_checker(sender, args) - -def msg_command_checker(sender, args): - return len(args) <= 1 or whisper(sender, args[0]) - -def reply_command_checker(sender, args): - return len(args) == 0 or reply(sender) - -def tpa_command_checker(sender, args): - if len(args) == 0: + def mail_interception(sender, args): + if len(args) < 3 or args[0].lower() != "send" or not is_player(sender): + return True + target = server.getPlayer(args[1]) + if target is not None and not can_send(sender, target): + msg(sender, "&c[&fBUSY&c] %s&r is busy!" % target.getDisplayName()) + return False return True - target = server.getPlayer(args[0]) - if target is not None and not can_send(sender, target): - msg(sender, "&c[&fBUSY&c] %s&r is busy!" % target.getDisplayName()) - return False - return True -def tpahere_command_checker(sender, args): - return tpa_command_checker(sender, args) + register = shared["modules"]["misc"].CommandInterceptions.register + register("essentials", "msg", msg_interception) + register("minecraft", "tell", msg_interception) + register("essentials", "reply", reply_interception) + register("essentials", "tpa", tpa_interception) + register("essentials", "tpahere", tpahere_interception) + register("essentials", "mail", mail_interception) -def mail_command_checker(sender, args): - if len(args) < 3 or args[0].lower() != "send": - return True - target = server.getPlayer(args[1]) - if target is not None and not can_send(sender, target): - msg(sender, "&c[&fBUSY&c] %s&r is busy!" % target.getDisplayName()) - return False - return True - - -@hook.event("player.PlayerCommandPreprocessEvent", "monitor") -def on_player_command_preprocess(event): - message = event.getMessage().split(" ") - if len(message) > 1 and message[0].lower() in ("/tell", "/minecraft:tell") and not whisper(event.getPlayer(), message[1]): - event.setCancelled(True) - - -def replace_ess_commands(): - - try: - map_field = server.getPluginManager().getClass().getDeclaredField("commandMap") - map_field.setAccessible(True) - command_map = map_field.get(server.getPluginManager()) - - commands_field = command_map.getClass().getDeclaredField("knownCommands") - commands_field.setAccessible(True) - map = commands_field.get(command_map) - - ess_msg_cmd = map.get("essentials:msg") - ess_reply_cmd = map.get("essentials:reply") - ess_tpa_cmd = map.get("essentials:tpa") - ess_tpahere_cmd = map.get("essentials:tpahere") - ess_mail_cmd = map.get("essentials:mail") - - msg_cmd_wrapper = CommandWrapper(ess_msg_cmd, msg_command_checker) - reply_cmd_wrapper = CommandWrapper(ess_reply_cmd, reply_command_checker) - tpa_cmd_wrapper = CommandWrapper(ess_tpa_cmd, tpa_command_checker) - tpahere_cmd_wrapper = CommandWrapper(ess_tpahere_cmd, tpahere_command_checker) - mail_cmd_wrapper = CommandWrapper(ess_mail_cmd, mail_command_checker) - - iterator = map.entrySet().iterator() - wrapped_commands = [] - while iterator.hasNext(): - entry = iterator.next() - value = entry.getValue() - changed = True - if value is ess_msg_cmd: - entry.setValue(msg_cmd_wrapper) - elif value is ess_reply_cmd: - entry.setValue(reply_cmd_wrapper) - elif value is ess_tpa_cmd: - entry.setValue(tpa_cmd_wrapper) - elif value is ess_tpahere_cmd: - entry.setValue(tpahere_cmd_wrapper) - elif value is ess_mail_cmd: - entry.setValue(mail_cmd_wrapper) - else: - changed = False - if changed: - wrapped_commands.append(entry.getKey()) - info("[imbusy] wrapped commands: /" + ", /".join(wrapped_commands)) - - except: - error("[Imbusy] Failed to wrap essentials commands") - error(trace()) +except: + error("[Imbusy] Failed to intercept commands:") + error(trace()) diff --git a/main.py b/main.py index 1b9e3f4..18bfcab 100644 --- a/main.py +++ b/main.py @@ -21,8 +21,6 @@ except: def on_enable(): if "blockplacemods" in shared["modules"]: shared["modules"]["blockplacemods"].schedule_torch_breaker() - if "imbusy" in shared["modules"]: - shared["modules"]["imbusy"].replace_ess_commands() if "serversigns" in shared["modules"]: shared["modules"]["serversigns"].check_all_signs_and_force_commands() info("RedstonerUtils enabled!") -- 2.45.2 From cffa3e27c87f60667fe77a717b75ea47e67e5629 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sat, 25 Jun 2016 11:57:43 +0200 Subject: [PATCH 5/8] Replaced serversigns interception implementation with the tool from misc too --- main.py | 2 +- serversigns.py | 36 +++++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/main.py b/main.py index 18bfcab..88b9c57 100644 --- a/main.py +++ b/main.py @@ -22,7 +22,7 @@ def on_enable(): if "blockplacemods" in shared["modules"]: shared["modules"]["blockplacemods"].schedule_torch_breaker() if "serversigns" in shared["modules"]: - shared["modules"]["serversigns"].check_all_signs_and_force_commands() + shared["modules"]["serversigns"].check_all_signs() info("RedstonerUtils enabled!") diff --git a/serversigns.py b/serversigns.py index 4fe952e..9241ee6 100644 --- a/serversigns.py +++ b/serversigns.py @@ -392,25 +392,31 @@ def can_build2(player, block): return not event.isCancelled() -def check_all_signs_and_force_commands(): +def check_all_signs(): + """ + Check if all registered signs have an associated sign block in the world. + WorldEdit commands could remove them without notification. + Pistons might also be able to achieve the same thing. + A sign missing from the world won't affect the world so it only checks on start. + """ + for loc in signs: if server.getWorld(loc[0]).getBlockAt(loc[1], loc[2], loc[3]).getType() not in (Material.WALL_SIGN, Material.SIGN_POST): del signs[loc] - try: - map_field = server.getPluginManager().getClass().getDeclaredField("commandMap") - map_field.setAccessible(True) - command_map = map_field.get(server.getPluginManager()) +try: + CommandInterceptions = shared["modules"]["misc"].CommandInterceptions + rsutils_cmd = CommandInterceptions.cmd_map.get("redstonerutils:serversigns") + label = rsutils_cmd.getLabel() - commands_field = command_map.getClass().getDeclaredField("knownCommands") - commands_field.setAccessible(True) - map = commands_field.get(command_map) + def interception(sender, args): + rsutils_cmd.execute(sender, label, args) + return False - rsutils_cmd = map.get("redstonerutils:serversigns") - map.put("svs", rsutils_cmd) - map.put("serversigns", rsutils_cmd) - map.put("signsmsg", rsutils_cmd) + def tab_completetion(original, sender, alias, args): + return rsutils_cmd.tabComplete(sender, alias, args) - except: - error("[Serversigns] failed to force commands") - error(trace()) + shared["modules"]["misc"].CommandInterceptions.register("serversigns", "serversigns", interception, tab_completion) +except: + error("[Serversigns] failed to force commands") + error(trace()) -- 2.45.2 From 3c7bd7409df00f3f7c42374322e90ce8a9038531 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sun, 26 Jun 2016 05:48:36 +0200 Subject: [PATCH 6/8] Fix serversigns derp --- main.py | 2 +- serversigns.py | 37 +++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/main.py b/main.py index 88b9c57..2cb80b4 100644 --- a/main.py +++ b/main.py @@ -22,7 +22,7 @@ def on_enable(): if "blockplacemods" in shared["modules"]: shared["modules"]["blockplacemods"].schedule_torch_breaker() if "serversigns" in shared["modules"]: - shared["modules"]["serversigns"].check_all_signs() + shared["modules"]["serversigns"].check_all_signs_and_intercept_command() info("RedstonerUtils enabled!") diff --git a/serversigns.py b/serversigns.py index 9241ee6..7626cef 100644 --- a/serversigns.py +++ b/serversigns.py @@ -392,7 +392,25 @@ def can_build2(player, block): return not event.isCancelled() -def check_all_signs(): +def check_all_signs_and_intercept_command(): + + try: + CommandInterceptions = shared["modules"]["misc"].CommandInterceptions + rsutils_cmd = CommandInterceptions.cmd_map.get("redstonerutils:serversigns") + label = rsutils_cmd.getLabel() + + def interception(sender, args): + rsutils_cmd.execute(sender, label, args) + return False + + def tab_completion(original, sender, alias, args): + return rsutils_cmd.tabComplete(sender, alias, args) + + shared["modules"]["misc"].CommandInterceptions.register("serversigns", "serversigns", interception, tab_completion) + except: + error("[Serversigns] failed to force commands") + error(trace()) + """ Check if all registered signs have an associated sign block in the world. WorldEdit commands could remove them without notification. @@ -403,20 +421,3 @@ def check_all_signs(): for loc in signs: if server.getWorld(loc[0]).getBlockAt(loc[1], loc[2], loc[3]).getType() not in (Material.WALL_SIGN, Material.SIGN_POST): del signs[loc] - -try: - CommandInterceptions = shared["modules"]["misc"].CommandInterceptions - rsutils_cmd = CommandInterceptions.cmd_map.get("redstonerutils:serversigns") - label = rsutils_cmd.getLabel() - - def interception(sender, args): - rsutils_cmd.execute(sender, label, args) - return False - - def tab_completetion(original, sender, alias, args): - return rsutils_cmd.tabComplete(sender, alias, args) - - shared["modules"]["misc"].CommandInterceptions.register("serversigns", "serversigns", interception, tab_completion) -except: - error("[Serversigns] failed to force commands") - error(trace()) -- 2.45.2 From 8b5131f8ae2f2a38cc53610def09fd1638743d16 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Tue, 28 Jun 2016 11:35:45 +0200 Subject: [PATCH 7/8] Removed an error and fixed /serversigns being completely inaccessible. --- plugin.yml | 3 +++ serversigns.py | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plugin.yml b/plugin.yml index bdd5acb..e342e84 100644 --- a/plugin.yml +++ b/plugin.yml @@ -2,3 +2,6 @@ name: RedstonerUtils main: main.py version: 3.1.0 author: redstone_sheep + +commands: + serversigns: diff --git a/serversigns.py b/serversigns.py index 7626cef..3aa4596 100644 --- a/serversigns.py +++ b/serversigns.py @@ -395,16 +395,16 @@ def can_build2(player, block): def check_all_signs_and_intercept_command(): try: - CommandInterceptions = shared["modules"]["misc"].CommandInterceptions - rsutils_cmd = CommandInterceptions.cmd_map.get("redstonerutils:serversigns") - label = rsutils_cmd.getLabel() + #CommandInterceptions = shared["modules"]["misc"].CommandInterceptions + #rsutils_cmd = CommandInterceptions.cmd_map.get("redstonerutils:serversigns") + #label = rsutils_cmd.getLabel() def interception(sender, args): - rsutils_cmd.execute(sender, label, args) + svs_command(sender, None, None, args) return False def tab_completion(original, sender, alias, args): - return rsutils_cmd.tabComplete(sender, alias, args) + return None shared["modules"]["misc"].CommandInterceptions.register("serversigns", "serversigns", interception, tab_completion) except: -- 2.45.2 From 1429f11180782a84b68b93ac8fc2cdde3916e2cf Mon Sep 17 00:00:00 2001 From: Dico200 Date: Wed, 29 Jun 2016 12:05:14 +0200 Subject: [PATCH 8/8] Fix hooking commands throwing UnsupportedOperationException. Server will instead tell us that the command 'serversigns' was not found in RedstonerUtils at the end of start, which is bullshit, everything works. --- plugin.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugin.yml b/plugin.yml index e342e84..bdd5acb 100644 --- a/plugin.yml +++ b/plugin.yml @@ -2,6 +2,3 @@ name: RedstonerUtils main: main.py version: 3.1.0 author: redstone_sheep - -commands: - serversigns: -- 2.45.2