from helpers import * from basecommands import simplecommand, Validate import org.bukkit.Material as Material import java.util.UUID as UUID import org.bukkit.Material as Material import org.bukkit.block.BlockFace as BlockFace from math import ceil commands_whitelist = ( "mail", "email", "memo", "echo", "ping", "cg join", "cg info", "chatgroup join", "chatgroup info", "i", "item", "p h", "plot home", "plot h", "p home", "plotme home", "plotme h", "up", "tppos", "seen", ) max_line_length = 256 max_lines = 20 help_page_size = 12 def load_signs(): signs_obj = open_json_file("serversigns", []) loaded = {} for entry in signs_obj: loaded[tuple(entry[:4])] = list(entry[4:]) return loaded def save_signs(): signs_obj = [] for key, value in signs.iteritems(): signs_obj.append(key + tuple(value)) save_json_file("serversigns", signs_obj) # {("world", x, y, z): ["owner_id", "msg1", "msg2"]} signs = load_signs() # Accumulated messages so players can have longer messages: {"Dico200": "Message...........", ""} lines = {} def fromLoc(bLoc): """ # Returns a tuple containing the (bukkit)location's world's name and its x, y and z coordinates # The format for the tuple is ("world_name", x, y, z) """ return (bLoc.getWorld().getName(), bLoc.getBlockX(), bLoc.getBlockY(), bLoc.getBlockZ()) def equals(loc1, loc2): """ # Returns whether loc1 and loc2 represent the same block """ for i in range(4): if loc1[i] != loc2[i]: return False return True def getOwner(sign): """ # Returns the name of the sign its owner """ return retrieve_player(sign[0]).getName() def isOwner(sign, player): """ # Returns whether the given player owns the sign """ return sign and sign[0] == uid(player) def canEdit(sign, player): """ # Returns whether the given player can edit the sign. # Returns False if the sign wasn't claimed. """ return (sign and player.hasPermission("utils.serversigns.admin")) or isOwner(sign, player) def getSign(locAt): """ # If data was found for a sign at the given location, returns the data. # This data follows the format of ["owner_id", "msg1", "msg2"...]. """ for loc, sign in signs.iteritems(): if equals(locAt, loc): return sign return None def identifySign(loc): """ # Returns a string from which the user can tell what sign you're talking about. # The string follows the format of "sign at (x,y,z) in world_name". """ return "sign at (%s) in %s" % (",".join((str(i) for i in loc[1:])), loc[0]) def signsMsg(msg, colour = '4'): """ # Returns the given msg, prefixed with '[Signs] '. # The given colour is after applied to the msg. # The colour defaults to 4 (dark red). """ return "&c[Signs] &" + colour + msg # /svs command subcommand_info = ( (("claim",), ( "Claims the sign so that you can add messages to it", ), (("[owner]", "claims.other"),)), (("info", "lines"), ( "Displays information about the (claimed) sign", )), (("add",), ( "Adds the message to the sign. Use ++ at the end", "to add the message to your buffer. You can then use", "the same command again to create a longer message", ), (("[++]", None),)), (("remove", "rem", "del", "delete"), ( "Removes the message with the given ID from the sign.", "The ID is given before each message by &b/svs info&a.", ), (("", None),)), (("switch", "reverse"), ( "Reverses the order in which the given messages are shown.", ), ((" ", None),)), (("clear",), ( "Removes all messages from the sign.", )), (("reset", "unclaim"), ( "Resets the sign, removing all messages and its owner.", )), (("whitelist", "commands", "wl"), ( "Displays a list of whitelisted commands", )), (("help",), ( "Displays this help page", ), (("[page]", None),)), ) @simplecommand(cmd = "serversigns", aliases = ["svs", "signmsg"], description = "Makes something happen when you right click signs. \nUse /svs help for more details.", usage = "[++]|remove |clear|info|help>", helpNoargs = True, senderLimit = 0) def svs_command(sender, command, label, args): arg1 = args[0].lower() cmd_info = None for cmd in subcommand_info: if arg1 in cmd[0]: cmd_info = cmd break Validate.notNone(cmd_info, signsMsg("That command could not be recognized, use &o/svs help &4for expected arguments")) cmd = cmd_info[0][0] Validate.isAuthorized(sender, "utils.serversigns." + cmd) #-------------------- Sub commands that don't require any conditions ----------------------- if cmd == "help": all_lines = [ "&aServer signs let's you add messages to a sign.", "Right clicking the sign will display all the messages. Commands", "can also be added, by prefixing the message with a '/'.", "How to use &b/serversigns&a:", ] for cmd in subcommand_info: if sender.hasPermission("utils.serversigns." + cmd[0][0]): params = "" if len(cmd) == 3: for param, perm in cmd[2]: if perm is None or sender.hasPermission("utils.serversigns." + perm): params += param + " " all_lines.append("&b/svs %s %s" % ("|".join(cmd[0]), params)) for info_line in cmd[1]: all_lines.append("&a- " + info_line) last_page = int(ceil(len(all_lines) / help_page_size)) info("last page: %d" % last_page) page = 1 if len(args) > 1 and args[1].isdigit(): page = int(args[1]) if page <= 0: page = 1 elif page > last_page: page = last_page first_line = signsMsg("Serversigns help page %d" % page, '6') if page < last_page: first_line += ", &e/svs help %d&6 for the next" % (page + 1) page_lines = [first_line] + all_lines[(page - 1) * help_page_size : min(page * help_page_size, len(all_lines))] return "\n".join(page_lines) if cmd == "whitelist": return signsMsg("Whitelisted commands: &3" + ", ".join(commands_whitelist), 'a') #------------------------------------------------------------------------------------------- block = sender.getTargetBlock(None, 5) Validate.isTrue(block.getType() in (Material.SIGN_POST, Material.WALL_SIGN), signsMsg("You have to be looking at a sign to use that!")) loc = fromLoc(block.getLocation()) sign = getSign(loc) signName = identifySign(loc) arg2 = args[1].lower() if len(args) > 1 else None #------------------------ Sub commands that require the block to be a sign ------------------------------- if cmd == "claim": Validate.isTrue(not sign, signsMsg("The %s was already claimed" % signName)) Validate.isTrue(can_build2(sender, block), signsMsg("You are not permitted to claim signs here")) target = sender if arg2: Validate.isTrue(player.hasPermission("utils.serversigns.admin"), signsMsg("You are not authorized to claim signs for other players")) target = server.getOfflinePlayer(arg2) Validate.notNone(target, signsMsg("That player could not be found")) Validate.isTrue(target.isOnline(), signsMsg("The target has to be online")) uuid = uid(target) if sign != None: if sign[0] == uuid: return signsMsg("The " + signName + " was already owned by that player") else: sign[0] = uuid else: signs[loc] = [uuid] save_signs() return signsMsg("Claimed the " + signName + ((" for %s" % target.getName()) if (target != sender) else ""), 'a') #---------------------------------------------------------------------------------------------------------- Validate.notNone(sign, signsMsg("The %s has not been claimed" % signName)) #----------------------Sub commands that require the sign to be claimed as well------------------------------------ if cmd == "info": sign_lines = "" for id, line in enumerate(sign[1:]): sign_lines += ("\n &a%s: \"&f%s&a\"" % (id + 1, line)) return signsMsg("Properties of the %s:\n Owner: %s\n Lines: %s" % (signName, getOwner(sign), sign_lines), 'a') #--------------------------------------------------------------------------------------------------------------- Validate.isTrue(canEdit(sign, sender), signsMsg("You do not own the %s!" % signName)) #---------------------- Sub commands that require you to own targeted sign as well ------------------------- if cmd == "add": Validate.isTrue(len(sign) - 1 <= max_lines, signsMsg("This sign already has the maximum amount of lines, you cannot add more")) line = " ".join(args[1:]) Validate.isTrue(line != "" and line != None, signsMsg("You have to enter a message to add or accumulate")) key = sender.getName() global lines Validate.isTrue(key in lines or line[:1] != "/" or sender.hasPermission("utils.serversigns.command"), signsMsg("You cannot add commands to a sign!")) if line[-2:] == "++": if key not in lines: lines[key] = "" Validate.isTrue(len(lines[key]) + len(line[:-2]) + 1 <= max_line_length, signsMsg("This line would be too long, so the given message was not added to the accumulated message")) lines[key] += " " + line[:-2] return signsMsg("Added given message to the message you're accumulating. \nYour accumulated message is now as follows: \n&f%s" % lines[key], 'a') if key in lines: line = (lines[key] + " " + line)[1:] Validate.isTrue(len(line) <= max_line_length, signsMsg("This line would be too long, so it was not added to the sign. It is however still accumulated.")) if line[0] == "/": cmd = line[1:].lower() whitelisted = False for wl_cmd in commands_whitelist: if cmd[:len(wl_cmd)] == wl_cmd: whitelisted = True break Validate.isTrue(whitelisted, signsMsg("That command is not whitelisted for use with serversigns")) if key in lines: del lines[key] sign.append(colorify(line) if line[0] != "/" else line) save_signs() return signsMsg("Added line \"&f%s&a\" to the %s" % (line, signName), 'a') if cmd == "remove": Validate.notNone(arg2, signsMsg("You have to enter the ID of the message to remove!")) try: id = int(arg2) except: return signsMsg("The ID of the message has to be a number and can be found by using &o/svs info") Validate.isTrue(id != 0 and id < len(sign), signsMsg("The %s has no message with an ID of %s, use &o/svs info &4for all messages." % (signName, id))) del sign[id] return signsMsg("Removed message with id %s from the %s" % (id, signName), 'a') if cmd == "switch": Validate.isTrue(len(args) == 3, signsMsg("You have to enter the 2 IDs of the messages to reverse")) try: id1 = int(args[1]) id2 = int(args[2]) except: return signsMsg("The ID of the message has to be a number and can be found by using &o/svs info") for id in (id1, id2): Validate.isTrue(id != 0 and id < len(sign), signsMsg("The %s has no message with an ID of %s, use &o/svs info &4for all messages." % (signName, id))) sign[id1], sign[id2] = sign[id2], sign[id1] save_signs() return signsMsg("Reversed the messages with IDs %s and %s of the %s" % (id1, id2, signName), 'a') if cmd == "clear": signs[loc] = [sign[0]] save_signs() return signsMsg("Removed all messages from the %s" % signName, 'a') if cmd == "reset": del signs[loc] save_signs() return signsMsg("Removed all messages and the owner from the %s, it can now be claimed" % signName, 'a') #------------------------------------------------------------------------------------------------------- @hook.event("player.PlayerInteractEvent") def on_click(event): if str(event.getAction()) != "RIGHT_CLICK_BLOCK": return block = event.getClickedBlock() if block.getType() not in (Material.WALL_SIGN, Material.SIGN_POST): return sign = getSign(fromLoc(block.getLocation())) if sign != None: player = event.getPlayer() for message in sign[1:]: if message[:1] == "/": server.dispatchCommand(player, message[1:]) else: msg(player, message, usecolor = False) # ---------------------------Sign breaking-------------------------------- checking_block = False faces = { BlockFace.NORTH : (0,1,2,), BlockFace.SOUTH : (3,), BlockFace.WEST : (4,), BlockFace.EAST : (5,), } @hook.event("block.BlockBreakEvent", "monitor") def on_break(event): if checking_block or event.isCancelled(): return block = event.getBlock() if block.getType() in (Material.SIGN_POST, Material.WALL_SIGN): check_sign(event, block, attached = False) for block_face, data_values in faces.iteritems(): block2 = block.getRelative(block_face) if block2.getType() == Material.WALL_SIGN and block2.getData() in data_values: check_sign(event, block2) block3 = block.getRelative(BlockFace.UP) if block3.getType() == Material.SIGN_POST: check_sign(event, block3) def check_sign(event, block, attached = True): player = event.getPlayer() loc = fromLoc(block.getLocation()) if block.getType() not in (Material.WALL_SIGN, Material.SIGN_POST) or getSign(loc) is None: return if not can_build2(player, block): event.setCancelled(True) msg(event.getPlayer(), signsMsg("You cannot break %s" % ("the sign attached to that block" if attached else "that sign"))) else: del signs[loc] save_signs() msg(player, signsMsg("Reset the %s which you just broke" % identifySign(loc))) def can_build2(player, block): global checking_block event = BlockBreakEvent(block, player) checking_block = True server.getPluginManager().callEvent(event) checking_block = False return not event.isCancelled() def check_all_signs_and_force_commands(): 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()) commands_field = command_map.getClass().getDeclaredField("knownCommands") commands_field.setAccessible(True) map = commands_field.get(command_map) rsutils_cmd = map.get("redstonerutils:serversigns") map.put("svs", rsutils_cmd) map.put("serversigns", rsutils_cmd) map.put("signsmsg", rsutils_cmd) except: error("[Serversigns] failed to force commands") error(trace())