diff --git a/basecommands.py b/basecommands.py index 89e08aa..7bfb8c2 100644 --- a/basecommands.py +++ b/basecommands.py @@ -1,13 +1,67 @@ from helpers import * -to_see_permission = "utils.showpermission" # See cmd permission in help +""" +@simplecommand is a decorator which is meant to replace @hook.command in redstoner-utils, where useful. +It takes care of checks such as whether the sender is a player, whether they have permission, +whether there are enough argumens, and also takes care of a help message. +On top of that, it makes the code shorter and easier to write with features like Validate, and returning a message instead of a boolean value. +@simplecommand has an inbuilt tracing feature, so you won't have to put all your code in a try/except statement anymore. +Make sure to `from basecommands import simplecommand` before using this decorator. +The arguments are as follows: +* cmd: the command, self explanatory (required); + +* aliases: A list containing any aliases for the command, like shortcuts; + +* usage: a String defining the expected arguments for the command. Example: + Let's say I have a command /tp . The usage is: " ". + I suggest we use the same format throughout redstoner-utils: + - Separate arguments by spaces; + - Use <> if the argument is required, and [] if the argument is optional; + - Add .. to the argument's identifier (name) if it is for example a message (containing spaces). + for example in /msg, the usage would be " " + +* description: a description of what the command does. Defaults to "Handles cmd". + This is used for the help message, where the description is (meant to be) indented. To keep this indentation + with longer descriptions, call the help message (with the command, ingame) and add '\n' + when it jumps to a new line in the chat. The decorator will take care of the indentation after that. + +* senderLimit: an integer resembling the accepted sender type. Defaults to -1. Use: + -1 for console as well as players; + 0 for players only; + 1 for console only. + +* amin: an integer resembling the minimum amount of arguments. Defaults to 0 + +* amax: an integer resembling the maximum amount of arguments. Defaults to -1, which means that there is no maximum. + +* helpNoargs: a boolean value resembling whether the help message should be displayed when no arguments are given. + Defaults to False. + +* helpSubcmd: a boolean value resembling whether the help message should be displayed when the first argument.lower() equals "help". + Defaults to False. + +Comments on the function added to the decorator: +It should return a message to send to the player. Color codes are translated automatically. It can return None or an empty string to send nothing. +Returning "HELP" makes it show the help message. + +Inside the function, calls to static methods in the class Validate can be used to make the code shorter and easier to write (maybe not easier to read). +For example, to make sure that a condition is met, use Validate.isTrue(condition, message to send to the player if the condition is not met) +Don't forget to `from basecommands import Validate` if you wish to make use of this. +For all other Validate checks, see the code below. Feel free to add your own. + +Instead of returning a message mid-code to describe an error, you can also use raise CommandException(msg), but it is almost always possible +to replace this return statement with a call to one of the functions in the Validate class. Once again, if you use raise CommandException(msg), +don't forget to `from basecommands import CommandException`. +""" + +to_see_permission = "utils.showpermission" # See cmd permission in help def isSenderValid(senderLimit, isPlayer): return True if senderLimit == -1 else senderLimit != isPlayer def invalidSenderMsg(isPlayer): - return "&cThat command can only be run from the console" if isPlayer else "&cThat command can only be run by players" + return "&cThat command can only be used by " + ("the console" if isPlayer else "players") def helpMsg(sender, cmd, description, usage, aliases, permission): help_msg = "&aInformation about command /%s:\n &9%s" % (cmd, description.replace("\n", "\n ")) @@ -65,7 +119,7 @@ def simplecommand(cmd, except CommandException, e: return e.message except Exception, e: - error(e.message, trace()) + error(trace()) return "&cAn internal error occurred while attempting to perform this command" return call diff --git a/calc.py b/calc.py index 1da634b..47326b3 100644 --- a/calc.py +++ b/calc.py @@ -11,7 +11,7 @@ calc_perm_power = "utils.calc.power" def calc(sender, text): try: - return do_calc(sender, text) + return do_calc(sender, text.lower()) except: return None diff --git a/chatalias.py b/chatalias.py new file mode 100644 index 0000000..dc7db72 --- /dev/null +++ b/chatalias.py @@ -0,0 +1,127 @@ +# Chat Aliasing plugin by Curs3d # +################################## +# Allows users to alias words, +# so that when they send a +# message in chat, it gets +# replaced by their specified +# word. Configuration of this +# plugin is in the "gnl" +# (general) tag of the JSON +# file named "aliases". The +# file is generated if not +# present. Set values to -1 +# for "unlimited" setting. + +from helpers import * +from traceback import format_exc as trace + +data = None + +def safe_open_json(): + global data + if data is not None: + return data + data = open_json_file("aliases") + if data is None: + data = {"gnl":{"max_len":"35","max_entries":"10"}} + save_json_file("aliases", data) + return data + + +@hook.command("alias", usage = "/ [to_alias] [alias...]", desc = "Aliases words in chat") +def on_alias_command(sender, cmd, label, args): + + if not is_player(sender): + msg(sender, "Sorry, Console cannot alias words") + return True + + if not sender.hasPermission("utils.alias.allowed"): + plugin_header(recipient = sender, name = "Chat Alias") + noperm(sender) + return True + + if len(args) == 0: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "This is a plugin that allows you to type in chat and have words replaced by other ones automatically!") + msg(sender, "\nCommands:") + msg(sender, "/alias : removes from aliased words. Use * to remove all aliased words.") + msg(sender, "/alias : Will change to in chat") + msg(sender, "\nYour Aliases:") + data = safe_open_json() + try: + for alias, value in data[str(sender.getUniqueId())].items(): + msg(sender, "%s ==> %s" % (alias, value)) + except KeyError: + pass + return True + + elif len(args) == 1: + data = safe_open_json() + if args[0] == "*": + try: + del data[str(sender.getUniqueId())] + except KeyError: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "No alias data to remove!") + return True + save_json_file("aliases", data) + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "ALL alias data successfuly removed!") + return True + + try: + if data[str(sender.getUniqueId())].pop(args[0], None) is None: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "Could not remove: alias not present!") + return True + except KeyError: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "Could not remove: you do not have any aliases!") + return True + + save_json_file("aliases", data) + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "Alias for %s successfuly removed" % args[0]) + return True + + elif len(args) >= 2: + data = safe_open_json() + alias = " ".join(args[1:]) + try: + if len(alias) > int(data["gnl"]["max_len"]) and int(data["gnl"]["max_len"]) >= 0: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "Please do not alias long words/sentences.") + return True + + if len(data[str(sender.getUniqueId())]) >= int(data["gnl"]["max_entries"]) and int(data["gnl"]["max_entries"]) >= 0: + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "You have reached the maximum amount of alias entries! Sorry!") + return True + except KeyError: + data[str(sender.getUniqueId())] = {} + + data[str(sender.getUniqueId())][args[0]] = alias + save_json_file("aliases", data) + plugin_header(recipient = sender, name = "Chat Alias") + msg(sender, "Chat Alias %s ==> %s successfully created!" % (args[0], alias)) + return True + + else: + return False + + +@hook.event("player.AsyncPlayerChatEvent", "high") +def on_player_chat(event): + playerid = str(event.getPlayer().getUniqueId()) + data = safe_open_json() + + if event.isCancelled(): + return + + try: + crashtest = data[playerid].items() + except KeyError: + return + + for alias, value in data[playerid].items(): + event.setMessage(event.getMessage().replace(alias, value)) diff --git a/helpers.py b/helpers.py index 4aba904..898dea7 100644 --- a/helpers.py +++ b/helpers.py @@ -6,6 +6,7 @@ import org.bukkit as bukkit import org.bukkit.Location as Location import org.bukkit.entity.Player as Player import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause as TeleportCause +import org.bukkit.event.block.BlockBreakEvent as BlockBreakEvent import org.bukkit.block as bblock import org.bukkit.event.entity as entity import org.bukkit.command.ConsoleCommandSender @@ -86,7 +87,7 @@ def colorify(text): """ replace &-codes with real color codes """ - return sub("&(?=[?\\da-fk-or])", u"\u00A7", "%s" % text) + return sub("&" + u"\u00A7", "&", "%s" % sub("&(?=[?\\da-fk-or])", u"\u00A7", "%s" % text)) def stripcolors(text): @@ -143,6 +144,15 @@ def is_player(obj): return (isinstance(obj, Player)) +def can_build(player, block): + """ + return True if the player can change/build at the location of given block + """ + event = BlockBreakEvent(block, player) + server.getPluginManager().callEvent(event) + return not event.isCancelled() + + def checkargs(sender, args, amin, amax): """ check if a command has a valid amount of args, otherwise notify the sender @@ -255,4 +265,27 @@ def toggle(player, ls, name = "Toggle", add = None): msg(player, "&a%s turned off!" % name) elif add != False: ls.append(pid) - msg(player, "&a%s turned on!" % name) \ No newline at end of file + msg(player, "&a%s turned on!" % name) + +def send_JSON_message(playername, message): + bukkit.Bukkit.getServer().dispatchCommand(bukkit.Bukkit.getServer().getConsoleSender(), "tellraw " + playername + " " + message) + + +def isIP(tocheck): + subsets = ["","","",""] + i = 0 + for j in range(0,len(tocheck)): + if not (tocheck[j] in "0123456789."): + return False + elif tocheck[j] == ".": + i += 1 + if (i >= 4): + return False + else: + subsets[i] += tocheck[j] + if not (i == 3): + return False + for j in range(0,3): + if not ((int(subsets[j]) >= 0) & (int(subsets[j]) <= 255)): + return False + return True diff --git a/imbusy.py b/imbusy.py new file mode 100644 index 0000000..64fa30b --- /dev/null +++ b/imbusy.py @@ -0,0 +1,107 @@ +# I'M BUSY! Plugin by Curs3d # +############################## +# Concept by CookieManors :D # +# http://bit.ly/1GnNPW8 # +############################## +# This plugin permits users to +# send a command that renders +# them "busy", not letting them +# to get tpa requests or direct +# messages, except from console. +# On restart, all busy data will +# be cleared. + +from helpers import * +from basecommands import simplecommand +from traceback import format_exc as trace +busy_players = [] + + +def unclear(): + msg(sender, "Umm, what? Sorry, directions unlclear, got head stuck in washing machine") + + +@hook.command("busy", + aliases = ["focus"], + usage = "/ ", + description = "Sets busy mode on, you cannot recieve tpas and MSGs" + ) +def on_busy_command(sender, cmd, label, args): + + if not is_player(sender): + msg(sender, "Sorry, Console cannot be busy") + return True + + if not sender.hasPermission("utils.busy.allowed"): + plugin_header(recipient = sender, name = "I'M BUSY!") + noperm(sender) + return True + + if len(args) == 0: + plugin_header(recipient = sender, name = "I'M BUSY!") + msg(sender, "This plugin allows being busy, and when turned on you will not recieve any direct messages or tpa requests.") + msg(sender, "\nCommands:") + msg(sender, "/busy on: turns on busy mode") + msg(sender, "/busy off: turns off busy mode") + msg(sender, "/busy status [player]: shows your or [player]'s current busy status.") + return True + + elif len(args) == 1: + if args[0] == "on": + if sender.getName() in busy_players: + plugin_header(recipient = sender, name = "I'M BUSY!") + msg(sender, "You cannot be even more focused than this without being a jedi!") + return True + busy_players.append(sender.getName()) + plugin_header(recipient = sender, name = "I'M BUSY!") + broadcast(None, "%s is now SUPER busy! Don't even TRY bothering them, it will not work!" % sender.getName()) + return True + + elif args[0] == "off": + plugin_header(recipient = sender, name = "I'M BUSY!") + try: + busy_players.remove(sender.getName()) + msg(sender, "Master has sent /busy command, %s is freeee!" % sender.getName()) + return True + except ValueError: + msg(sender, "You are not busy! You cannot be even less busy! Are you perhaps bored?") + return True + + elif args[0] == "status": + plugin_header(recipient = sender, name = "I'M BUSY!") + if sender.getName() in busy_players: + msg(sender, "You are super-duper busy and concentrated right now. Think, think, think!") + return True + else: + msg(sender, "You are completely unable to focus right now.") + return True + + else: + plugin_header(recipient = sender, name = "I'M BUSY!") + unclear() + return False + + elif len(args) == 2 and args[0] == "status": + plugin_header(recipient = sender, name = "I'M BUSY!") + if args[1] in busy_players: + msg(sender, "Yes, %s is busy. Shhh..." % args[1]) + return True + else: + msg(sender, "No, you're good. Feel free to chat with %s!" % args[1]) + return True + + else: + plugin_header(recipient = sender, name = "I'M BUSY!") + unclear() + return False + + +@hook.event("player.PlayerCommandPreprocessEvent", "monitor") +def on_cmd_preprocess_event(event): + message = event.getMessage().split(" ") + if message[0] == "/msg" or message[0] == "/w" or message[0] == "/m" or \ + message[0] == "/tell" or message[0] == "/tpa" or message[0] == "/tpahere": + if message[1] in busy_players: + plugin_header(recipient = event.getPlayer(), name = "I'M BUSY!") + msg(event.getPlayer(), "We are sorry, but %s is currently busy. Please try again later." % message[1]) + event.setCancelled(True) diff --git a/iptracker.py b/iptracker.py new file mode 100644 index 0000000..70cab1a --- /dev/null +++ b/iptracker.py @@ -0,0 +1,107 @@ +import mysqlhack +import org.bukkit as bukkit +import json +from java.util import UUID as UUID +from helpers import * +from org.bukkit import * +from traceback import format_exc as trace +from iptracker_secrets import * + + +iptrack_permission = "utils.iptrack" + + +@hook.event("player.PlayerJoinEvent", "low") +def on_player_join(event): + t = threading.Thread(target=on_player_join_thread, args=(event)) + t.daemon = True + t.start() + +def on_player_join_thread(event): + player = event.getPlayer() + ip = player.getAddress().getHostString() + uuid = uid(player) + conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") + curs = conn.cursor() + curs.execute("SELECT ips FROM uuid2ips WHERE uuid = ?", (uuid, )) + results = curs.fetchall() + if len(results) == 0: + ips = [] + else: + ips = json.loads(results[0][0]) + curs.execute("SELECT uuids FROM ip2uuids WHERE ip = ?", (ip, )) + results = curs.fetchall() + if len(results) == 0: + uuids = [] + else: + uuids = json.loads(results[0][0]) + new_ip_entry = (len(ips) == 0) + new_uuid_entry = (len(uuids) == 0) + if ip not in ips: + ips.append(ip) + if new_ip_entry: + curs.execute("INSERT INTO uuid2ips VALUES (?,?)", (uuid, json.dumps(ips), )) + else: + curs.execute("UPDATE uuid2ips SET ips = ? WHERE uuid = ?", (uuid, json.dumps(ips), )) + if uuid not in uuids: + uuids.append(uuid) + if new_uuid_entry: + curs.execute("INSERT INTO ip2uuids VALUES (?,?)", (ip, json.dumps(uuids), )) + else: + curs.execute("UPDATE ip2uuids SET uuids = ? WHERE uuid = ?", (ip, json.dumps(uuids), )) + conn.commit() + curs.close() + conn.close() + + +@hook.command("getinfo") +def on_getinfo_command(sender, args): + t = threading.Thread(target=on_player_join_thread, args=(sender, args)) + t.daemon = True + t.start() + +def on_getinfo_command_thread(sender, args): + if(sender.hasPermission(iptrack_permission)): + if not checkargs(sender, args, 1, 1): + return False + else: + if isIP(args[0]): + conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") + curs = conn.cursor() + curs.execute("SELECT uuids FROM ip2uuids WHERE ip = ?", (args[0], )) + results = curs.fetchall() + curs.close() + conn.close() + if len(results) == 0: + msg(sender, "IP " + args[0] + " is not registered in the database, maybe you got a number wrong?") + else: + uuids = json.loads(results[0][0]) + msg(sender, "IP " + args[0] + " was seen with " + str(len(uuids)) + " different Accounts:") + for i in range(0, len(uuids)): + p=Bukkit.getOfflinePlayer(UUID.fromString(uuids[i])) + if is_player(sender): + send_JSON_message(sender.getName(), '["",{"text":"' + p.getName() + ' - (uuid: ' + uuids[i] + '","color":"gold","clickEvent":{"action":"run_command","value":"/getinfo ' + p.getName() + '"},"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"To search for ' + p.getName() + ' in the database, simply click the name!","color":"gold"}]}}}]') + else: + msg(sender,p.getName() + " - (uuid: " + uuids[i] + ")") + else: + target = Bukkit.getOfflinePlayer(args[0]) + uuid = target.getUniqueId() + conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") + curs = conn.cursor() + curs.execute("SELECT ips FROM uuid2ips WHERE uuid = ?", (uuid.toString(), )) + results = curs.fetchall() + curs.close() + conn.close() + if len(results) == 0: + msg(sender, "Player " + args[0] + " is not registered in the database, maybe you misspelled the name?") + else: + ips = json.loads(results[0][0]) + msg(sender, "Player " + args[0] + " was seen with " + str(len(ips)) + " different IPs:") + for i in range(0, len(ips)): + if is_player(sender): + send_JSON_message(sender.getName(), '["",{"text":"' + ips[i] + '","color":"gold","clickEvent":{"action":"run_command","value":"/getinfo ' + ips[i] + '"},"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"To search for the IP ' + ips[i] + ' in the database, simply click the IP!","color":"gold"}]}}}]') + else: + msg(sender,ips[i]) + else: + noperm(sender) + return True diff --git a/main.py b/main.py index 3445447..4769bc5 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,6 @@ except: @hook.enable def on_enable(): - shared["modules"]["vanishfix"].enabled() info("RedstonerUtils enabled!") @@ -39,10 +38,12 @@ shared["load_modules"] = [ "adminchat", # Adds /badge, allows to give players achievements "badges", - # Adds a few block placement corrections/mods - "blockplacemods", + # Adds a few block placement corrections/mods + "blockplacemods", # Adds /calc, toggles automatic solving of Math expressions in chat "calc", + # Adds aliasing of chat words + "chatalias", # Plugin to locate laggy chunks. /lc lists chunks with more than n entities "lagchunks", # Adds /report and /rp, Stores reports with time and location @@ -53,6 +54,8 @@ shared["load_modules"] = [ "webtoken", # Adds /lol, broadcasts random funyy messages. A bit like the splash text in the menu "saylol", + # Adds /signalstrength, lets you request a signal strength and an amount of items will be inserted into target container to meet that strength. + "signalstrength", # Shows the owner of a skull when right-clicked "skullclick", # Adds /listen, highlights chat and plays a sound when your name was mentioned @@ -71,6 +74,8 @@ shared["load_modules"] = [ "check", # Adds /an, a command you can use to share thoughts/plans/news "adminnotes", + # Adds busy status to players + "imbusy", # Adds /imout, displays fake leave/join messages "imout", #adds snowbrawl minigame @@ -88,9 +93,13 @@ shared["load_modules"] = [ # Per-player notes "tag", # vanish toggle module - temporary fix - "vanishfix", + #"vanishfix", # obisidian mining punishment plugin - "punishments" + "punishments", + # a simple replacement for the buggy essentials /vanish + "vanish", + # ip-tracking utility + "iptracker" ] shared["modules"] = {} for module in shared["load_modules"]: diff --git a/mentio.py b/mentio.py index dbcd4ce..fa46d71 100644 --- a/mentio.py +++ b/mentio.py @@ -4,7 +4,7 @@ from traceback import format_exc as print_traceback mentions = open_json_file("mentio", {}) # contains a list of keywords for each player (uuid) -max_amount = 3 +max_amount = 1000 arrow = colorify(u"&r&7\u2192&r") colors_reg = reg_compile(u"\u00A7[\\da-fk-or]") # finds color codes @@ -13,7 +13,7 @@ def saveMentions(): save_json_file("mentio", mentions) -@hook.event("player.AsyncPlayerChatEvent", "high") +@hook.event("player.AsyncPlayerChatEvent", "monitor") def onChat(event): if not event.isCancelled(): sender = event.getPlayer() @@ -70,7 +70,7 @@ def add_keyword(sender, args): keywords = get_keywords(sender) new_word = stripcolors(args[1].lower()) - if len(keywords) >= max_amount: + if (len(keywords) >= max_amount) and (max_amount >= 0): msg(sender, "&cYou are already listening for %s words! Try &6/mentio del " % max_amount) return True @@ -146,4 +146,4 @@ def onListenCommand(sender, command, label, args): show_help(sender) else: show_help(sender) - return True \ No newline at end of file + return True diff --git a/misc.py b/misc.py index 6145f86..e5727bc 100644 --- a/misc.py +++ b/misc.py @@ -6,7 +6,7 @@ from sys import exc_info import thread import org.bukkit.inventory.ItemStack as ItemStack import org.bukkit.Bukkit as Bukkit -from basecommands import simplecommand +from basecommands import simplecommand, Validate @@ -70,7 +70,6 @@ def cmd_event2(event): - """ Disabled while builder can't access Trusted @hook.event("player.PlayerGameModeChangeEvent", "low") def on_gamemode(event): @@ -203,7 +202,8 @@ def eval_thread(sender, code): pythoners = [ "e452e012-2c82-456d-853b-3ac8e6b581f5", # Nemes "ae795aa8-6327-408e-92ab-25c8a59f3ba1", # jomo -"305ccbd7-0589-403e-a45b-d791dcfdee7d" # PanFritz +"305ccbd7-0589-403e-a45b-d791dcfdee7d", # PanFritz +"51f2ad3c-6cc8-40ea-aa2b-f25970316921" # Dico ] @simplecommand("pyeval", @@ -256,14 +256,26 @@ def on_modules_command(sender, command, label, args): plugin_header(sender, "Modules") msg(sender, ", ".join([(("&a" if mod in shared["modules"] else "&c") + mod) for mod in shared["load_modules"]])) -@hook.command("warn") -def on_warn_command(sender, command, label, args): + +@simplecommand("warn", + usage = "", + description = "Warns everybody on the server that you will cause lag shortly", + amax = 0, + helpSubcmd = True) +def warn_command(sender, command, label, args): broadcast(None, " &b= &2&lLag incoming! &r-%s" % sender.getDisplayName()) -@hook.command("warnp") -def on_warnp_command(sender, command, label, args): + +@simplecommand("warnp", + usage = "", + description = "Warns everybody on the server that you might cause lag shortly", + amax = 0, + helpSubcmd = True) +def warnp_command(sender, command, label, args): broadcast(None, " &b= &2&lPossible lag incoming! &r-%s" % sender.getDisplayName()) + + """ Something I'm planning for schematics @hook.event("player.PlayerCommandPreprocessEvent", "low") def on_command(event): diff --git a/player.py b/player.py index 0897493..0f4a47f 100644 --- a/player.py +++ b/player.py @@ -25,4 +25,5 @@ def on_join(event): @hook.event("player.PlayerQuitEvent","highest") def on_leave(event): - py_players.remove(get_py_player(event.getPlayer())) \ No newline at end of file + py_players.remove(get_py_player(event.getPlayer())) + diff --git a/punishments.py b/punishments.py index 4c39092..4b57be1 100644 --- a/punishments.py +++ b/punishments.py @@ -98,7 +98,7 @@ def command(sender, cmd, label, args): msg(sender, "&e-&a There are no people mining obsidian") return True for slave in slaves: - msg(sender, "&e-&a %s: %s blocks" % (slave.get_uuid(), slave.get_blocks())) + msg(sender, "&e-&a %s: %s blocks" % (server.getOfflinePlayer(juuid(slave.get_uuid())).getName(), slave.get_blocks())) return True elif args[0] == "add": player = server.getOfflinePlayer(str(args[1])) @@ -106,6 +106,7 @@ def command(sender, cmd, label, args): player.teleport(server.getWorld(punish_world).getSpawnLocation()) Slave(False, player, int(args[2])) save_slaves() + msg(player, "&e-&a You have been punished, mine %s blocks of obsidian to get out!" % args[2]) msg(sender, "&e-&a Player %s has been added into punishments for %s blocks of obsidian" % (player.getName(), args[2])) else: msg(sender, "&cYou can only punish online players") diff --git a/scriptutils.py b/scriptutils.py index 2e0dd2d..312f6c4 100644 --- a/scriptutils.py +++ b/scriptutils.py @@ -149,7 +149,7 @@ arg 0 size of backup @hook.command("script_backup_database_end") def print_backup_db_end(sender, command, label, args): if not is_player(sender): - broadcast(None, "&6 =&2 Databse backup completed.") + broadcast(None, "&6 =&2 Database backup completed.") server.dispatchCommand(server.getConsoleSender(), "ac &abackup size: &2%sMB&a." % args[0]) else: noperm(sender) diff --git a/serversigns.py b/serversigns.py new file mode 100644 index 0000000..c7bc23c --- /dev/null +++ b/serversigns.py @@ -0,0 +1,221 @@ +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 java.util.HashSet as JSet + + +cmd_use_perm = "utils.svs.cmd" +msg_use_perm = "utils.svs.msg" + +signs = open_json_file("serversigns", {}) # {("world", x, y, z): ["owner_id", "msg1", "msg2"]} + +lines = {} #Accumulated messages so players can have longer messages: {"Dico200": "Message...........", ""} + +transparent_blocks_set = JSet([Material.AIR, Material.GLASS, Material.STAINED_GLASS]) #used in sender.getTargetBlock() + +@simplecommand(cmd = "serversigns", aliases = ["svs", "signmsg"], + description = "Makes something happen when you right click certain signs", + usage = "[claim|unclaim|add |remove |clear|info|help]", + helpNoargs = True, + senderLimit = 0) +def svs_command(sender, command, label, args): + try: + arg1 = args[0].lower() + if arg1 not in ("add", "remove", "clear", "claim", "unclaim", "help"): + return "&4That argument could not be recognized, use &o/svs &4help for more information" + + sender = server.getPlayer(sender.getName()) + block = sender.getTargetBlock(transparent_blocks_set, 8) + info("Block type: " + str(block.getType())) + if block.getType() not in (Material.SIGN_POST, Material.WALL_SIGN): + return "&4You have to be looking at a sign to use that!" + + loc = fromLoc(block.getLocation()) + sign = getSign(loc) + arglen = len(args) + arg2 = args[1].lower() if arglen > 1 else None + + + if arg1 == "claim": + Validate.isAuthorized(sender, "utils.serversigns.claim") + target = sender + if arg2: + Validate.isAuthorized(sender, "utils.serversigns.admin") + target = server.getOfflinePlayer(arg2) + Validate.notNone(target, signsMsg("That player could not be found", '4')) + + Validate.isPlayer(target) + uuid = uid(sender) + if sign != None: + if sign[0] == uuid: + return signsMsg(identifySign(loc, True) + " was already owned by that player", '4') + else: + sign[0] = uuid + else: + signs[loc] = [uuid] + + return signsMsg("Claimed " + identifySign(loc)) + + + elif arg1 == "unclaim": + Validate.isAuthorized(sender, "utils.serversigns.unclaim") + Validate.isTrue(canEdit(sign, sender), signsMsg("You cannot unclaim the %s!" % identifySign(loc)), '4') + + if not (("-c" in args) and sender.hasPermission("utils.serversigns.admin")): + del signs[locAt] + return signsMsg("The %s was reset successfully" % identifySign(loc)) + sign[0] = "" + return signsMsg("The %s had its owner removed successfully" % identifySign(loc)) + + + elif arg1 == "help": + admin = sender.hasPermission("utils.serversigns.admin") + + return + + + elif arg1 == "add": + Validate.isTrue(canEdit(sign, sender), signsMsg("You cannot edit the %s!" % identifySign(loc)), '4') + line = " ".join(args[1:]) + Validate.isTrue(line != "" and line != None, signsMsg("You have to enter a message to add or accumulate!", '4')) + key = sender.getName() + + Validate.isTrue(key in lines or line[:1] != "/" or sender.hasPermission("utils.serversigns.command"), signsMsg("You cannot add commands to a sign!", '4')) + + if line[-2:] == "++": + if key not in lines: + lines[key] = "" + lines[key] += " " + line[:-2] + elif key in lines: + line = lines[key] + " " + line + sign.append(colorify(line) if line[0] != "/" else line) + return signsMsg("Added line \"%s&a\" to the %s" % (line, identifySign(loc))) + + + elif arg1 == "info": + Validate.notNone(sign, signsMsg("The %s has not been claimed" % identifySign(loc), '4')) + lines = "" + for id, line in enumerate(sign[1:]): + lines += ("\n &a%s: \"%s&a\"" % (id + 1, line)) + msg = signsMsg("Some information about the %s:\n Owner: %s\n Lines: %s" % identifySign(loc), getOwner(sign), lines) + + + elif arg1 == "remove": + Validate.notNone(arg2, signsMsg("You have to enter the ID of the message to remove!", '4')) + 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", '4') + 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." % (identifySign(loc), id), '4')) + sign.remove(id) + return signsMsg("Removed message with id %s from the %s" % (id, identifySign(loc))) + except: + error(trace()) + + + +@hook.event("player.PlayerInteractEvent") +def onClick(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) + +def fromLoc(bLoc): #Bukkit Location to ("world", x, y, z) + return (bLoc.getWorld().getName(), bLoc.getBlockX(), bLoc.getBlockY(), bLoc.getBlockZ()) + +def equals(loc1, loc2): + for i in range(4): + if loc1[i] != loc2[i]: + return False + return True + +def getOwner(sign): + return retrieve_player(sign[0]).getName() + +def isOwner(sign, player): + return sign and sign[0] == uid(player) + +def canEdit(sign, player): + return player.hasPermission("utils.serversigns.admin") or isOwner(sign, player) + +def getSign(locAt): + for loc, sign in signs.iteritems(): + if equals(locAt, loc): + return sign + return None + +def identifySign(loc, capital = False): + return "%sign at (%s) in %s" % ("S" if capital else "s", ",".join(loc[1:]), loc[0]) + +def signsMsg(msg, colour = 'a'): + return "&c[Signs] &" + colour + msg + + + + + + +""" +def eventhook(event, priority = "normal"): + + if "." not in event: + word = "" + for s in event: + if word != "" and s in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": + break; + word += s.lower() + event = "%s.%s" % (word, event) + + def decorator(function): + + @hook.event(event, priority) + def hook(event): + try: + function(event) + except EventException, e: + pass + + return hook + + return decorator + +class EventException(Exception): + def __init__(self, msg): + self.msg = msg + +"" +@eventhook("PlayerInteractEvent") +def x(event): + + p = event.getPlayer() + if p == None: + raise EventException(Stuff) +"" +""" + + + + + + + + + + + + + + + diff --git a/signalstrength.py b/signalstrength.py new file mode 100644 index 0000000..d51f35a --- /dev/null +++ b/signalstrength.py @@ -0,0 +1,159 @@ +from helpers import * +import org.bukkit.inventory.ItemStack as ItemStack +import org.bukkit.Material as Material +from math import ceil +from basecommands import simplecommand, Validate, CommandException + +""" Suggestion by Armadillo28, see thread: http://redstoner.com/forums/threads/2213?page=1#reply-14507 """ + +disallowed_item_types = ( + Material.getMaterial(0), + Material.getMaterial(175), + Material.getMaterial(383), +) + +default_args = open_json_file("signalstrengthdefaults", {}) + +def save_defaults(): + save_json_file("signalstrengthdefaults", default_args) + + +def item_name(item_type, remove_underscores = True): + typ = str(item_type).lower() + return typ.replace("_", "") if remove_underscores else typ + + +def item_type_allowed(item_type): + return not item_type in disallowed_item_types + + +def required_item_count(strength, stack_size, slot_count): + if strength == 0: + item_count = 0 + elif strength == 1: + item_count = 1 + else: + item_count = int(ceil(slot_count * stack_size / 14.0 * (strength - 1))) + + resulting_strength = 0 if item_count == 0 else int(1 + 14.0 * item_count / stack_size / slot_count) + #Clarification on these formulas at http://minecraft.gamepedia.com/Redstone_Comparator#Containers + + return item_count if resulting_strength == strength else None + + +def get_data(player, args): + uuid = uid(player) + if uuid in default_args: + strength, item_type, item_data = default_args[uuid] + item_type = Material.getMaterial(item_type) + else: + strength = 1 + item_type = Material.REDSTONE + item_data = 0 + + if len(args) > 0: + Validate.isTrue(args[0].isdigit() and 0 <= int(args[0]) <= 15, "&cThe signal strength has to be a value from 0 to 15") + strength = int(args[0]) + + if len(args) > 1: + if args[1].isdigit(): + item_type = Material.getMaterial(int(args[1])) + else: + item_type = Material.matchMaterial(args[1]) + Validate.notNone(item_type, "&cThat item type could not be found") + Validate.isTrue(item_type not in disallowed_item_types, "&cThat item type may not be used") + + if len(args) > 2: + Validate.isTrue(args[2].isdigit() and 0 <= int(args[2]) <= 15, "&cThe data has to be a value from 0 to 15") + item_data = int(args[2]) + + return (strength, item_type, item_data) + + +def get_inventory(block): + try: + return block.getState().getInventory() + except AttributeError: + return None + + +def get_entire_container(container): + container_blocks = [container] + container_type = container.getType() + if container_type in (Material.CHEST, Material.TRAPPED_CHEST): + loc = container.getLocation() + x = loc.getBlockX() + y = loc.getBlockY() + z = loc.getBlockZ() + world = loc.getWorld() + + container_blocks += [ + block for block in ( + world.getBlockAt(x + 1, y, z), + world.getBlockAt(x - 1, y, z), + world.getBlockAt(x, y, z + 1), + world.getBlockAt(x, y, z - 1), + ) if block.getType() == container_type + ] + + return container_blocks + + + +@simplecommand("signalstrength", + usage = "(default) [signalstrength] [item] [data]", + aliases = ["ss", "level"], + description = "Fills the targeted container with the correct amount of items to achieve the desired signal strength.", + amin = 0, + amax = 4, + helpSubcmd = True, + senderLimit = 0) +def on_signalstrength_command(sender, command, label, args): + + if len(args) > 0 and args[0].lower() in ("default", "defaults", "setdefaults"): + strength, item_type, item_data = get_data(sender, args[1:]) + + uuid = uid(sender) + if strength == 1 and item_type == Material.REDSTONE and item_data == 0: + if uuid in default_args: + del default_args[uuid] + save_defaults() + else: + default_args[uuid] = (strength, str(item_type), item_data) + save_defaults() + + return "&aSet your signal strength defaults to (%s, %s, %s)" % (strength, item_name(item_type, False), item_data) + + Validate.isTrue(len(args) <= 3, "&cExpected at most 3 arguments") + + target_block = sender.getTargetBlock(None, 5) + Validate.notNone(target_block, "&cThat command can only be used when a container is targeted") + + inventory = get_inventory(target_block) + Validate.notNone(inventory, "&cThat command can only be used if a container is targeted") + + strength, item_type, item_data = get_data(sender, args) + + #--------Get the stack size and required amount of items to achieve the desired signal strength--------- + stack_size = item_type.getMaxStackSize() + slot_count = inventory.getSize() + + item_count = required_item_count(strength, stack_size, slot_count) + Validate.notNone(item_count, "&cThe desired signal strength could not be achieved with the requested item type") + + #--------Add the other side of the chest if target is a double chest and check if player can build--------- + container_blocks = get_entire_container(target_block) + for block in container_blocks: + Validate.isTrue(can_build(sender, block), "&cYou do not have permission to do that here") + + #----------------Insert items------------- + full_stack_count, remaining = divmod(item_count, stack_size) + for block in container_blocks: + inv = block.getState().getInventory() + inv.clear() + for i in range(full_stack_count): + inv.setItem(i, ItemStack(item_type, stack_size, item_data)) + if remaining > 0: + inv.setItem(full_stack_count, ItemStack(item_type, remaining, item_data)) + + return "&aComparators attached to that %s will now put out a signal strength of %s" % (item_name(target_block.getType()), strength) diff --git a/tag.py b/tag.py index 0a312aa..b2aa98d 100644 --- a/tag.py +++ b/tag.py @@ -34,11 +34,11 @@ def command(sender, command, label, args): else: msg(sender, "&a-&c Unknown subcommand! (add, check, del)") else: - msg(sender, "&a&c Usage: /tag add/check") + msg(sender, "&a&c Usage: /tag add/check/del") return True def delete(sender, args): - player = server.getPlayer(args[0]) + player = server.getOfflinePlayer(args[0]) uuid = uid(player) try: if data[uuid] == None: @@ -54,7 +54,7 @@ def delete(sender, args): msg(sender, "&a-&e Deleted note at %s" % args[1]) def add(sender, args): - player = server.getPlayer(args[0]) + player = server.getOfflinePlayer(args[0]) uuid = uid(player) try: if data[uuid] == None: @@ -66,7 +66,7 @@ def add(sender, args): save_json_file("tag", data) def check(sender, args): - player = server.getPlayer(args[0]) + player = server.getOfflinePlayer(args[0]) uuid = uid(player) try: num = 0 diff --git a/vanish.py b/vanish.py new file mode 100644 index 0000000..a572ebc --- /dev/null +++ b/vanish.py @@ -0,0 +1,121 @@ +from helpers import * +from basecommands import simplecommand +from basecommands import Validate + +vanished = [] + + +def is_authorized(player): + return player.hasPermission("utils.vanish") + + +def is_vanished(player): + return uid(player) in vanished + + +#this can be used to silently set the vanished state of a player +def set_state(player, state): + if state == is_vanished(player): + return + + if state: + enable_vanish(player) + else: + disable_vanish(player) + + +def enable_vanish(target): + vanished.append(uid(target)) + for player in list(server.getOnlinePlayers()): + if not is_authorized(player): + player.hidePlayer(target) + + +def disable_vanish(target): + vanished.remove(uid(target)) + for player in list(server.getOnlinePlayers()): + player.showPlayer(target) + + +@simplecommand("vanish", + aliases = ["v"], + usage = "[on/off]", + description = "Toggles vanish mode, hiding you and your online status \nfrom other players.", + senderLimit = 0, + amin = 0, + amax = 1, + helpNoargs = False, + helpSubcmd = True +) +def vanish_command(sender, command, label, args): + try: + current_state = is_vanished(sender) + new_state = not current_state + + if len(args) == 1: + arg = args[0].lower() + if arg == "on": + new_state = True + elif arg == "off": + new_state = False + + if current_state == new_state: + return "&cYou were %s vanished!" % ("already" if current_state else "not yet") + + set_state(sender, new_state) + return "&a%s vanish mode!" % ("Enabled" if new_state else "Disabled") + except: + error(trace()) + + +@hook.event("player.PlayerJoinEvent") +def on_player_join(event): + player = event.getPlayer() + + if not is_authorized(player): + for uuid in vanished: + player.hidePlayer(retrieve_player(uuid)) + + elif is_vanished(player): + msg(player, "&cKeep in mind that you are still vanished! Use /vanish to disable.") + + +@hook.event("player.PlayerQuitEvent") +def on_player_quit(event): + player = event.getPlayer() + + if not is_authorized(player): + for uuid in vanished: + player.showPlayer(retrieve_player(uuid)) + + +@simplecommand("vanishother", + usage = "{player} [on/off]", + description = "Toggles vanish mode for someone, hiding them and their online status from other players.", + amin = 1, + amax = 2, + helpNoargs = True, + helpSubcmd = True) +def vanishother_command(sender, command, label, args): + target = server.getPlayer(args[0]) + Validate.notNone(target, "&cThe specified player is not online") + + current_state = is_vanished(target) + new_state = not current_state + + if len(args) == 2: + arg = args[1].lower() + if arg == "on": + new_state = True + elif arg == "off": + new_state = False + + if current_state == new_state: + return "&cThat player was already vanished!" if current_state else "&cThat player was not yet vanished!" + + set_state(target, new_state) + + enabled_str = "enabled" if new_state else "disabled" + if target != sender: + msg(target, "&aVanish mode %s by %s" % (enabled_str, sender.getDisplayName() if is_player(sender) else "&9CONSOLE")) + return "&aVanish mode %s%s" % (enabled_str, " for " + target.getDisplayName() if target != sender else "")