diff options
-rw-r--r-- | adminchat.py | 12 | ||||
-rw-r--r-- | adminnotes.py | 2 | ||||
-rw-r--r-- | basecommands.py | 74 | ||||
-rw-r--r-- | blockplacemods.py | 34 | ||||
-rw-r--r-- | calc.py | 68 | ||||
-rw-r--r-- | chatalias.py | 138 | ||||
-rw-r--r-- | chatgroups.py | 32 | ||||
-rw-r--r-- | check.py | 5 | ||||
-rw-r--r-- | helpers.py | 76 | ||||
-rw-r--r-- | imbusy.py | 107 | ||||
-rw-r--r-- | iptracker.py | 107 | ||||
-rw-r--r-- | loginsecurity.py | 25 | ||||
-rw-r--r-- | main.py | 59 | ||||
-rw-r--r-- | mentio.py | 8 | ||||
-rw-r--r-- | misc.py | 51 | ||||
-rw-r--r-- | mysql_utils.py | 33 | ||||
-rw-r--r-- | mysqlhack.py | 8 | ||||
-rw-r--r-- | player.py | 1 | ||||
-rw-r--r-- | punishments.py | 129 | ||||
-rw-r--r-- | scriptutils.py | 212 | ||||
-rw-r--r-- | servercontrol.py | 115 | ||||
-rw-r--r-- | serversigns.py | 297 | ||||
-rwxr-xr-x | setup.sh | 175 | ||||
-rw-r--r-- | signalstrength.py | 159 | ||||
-rw-r--r-- | snowbrawl.py | 52 | ||||
-rw-r--r-- | synchronizeranks.py | 84 | ||||
-rw-r--r-- | tag.py | 78 | ||||
-rw-r--r-- | thread_utils.py | 24 | ||||
-rw-r--r-- | vanish.py | 121 | ||||
-rw-r--r-- | vanishfix.py | 14 | ||||
-rw-r--r-- | wrapper.py | 11 | ||||
-rw-r--r-- | wrapper_command.py | 11 | ||||
-rw-r--r-- | wrapper_event.py | 22 | ||||
-rw-r--r-- | wrapper_player.py | 89 |
34 files changed, 2252 insertions, 181 deletions
diff --git a/adminchat.py b/adminchat.py index 3b07f18..b5c9d55 100644 --- a/adminchat.py +++ b/adminchat.py @@ -50,12 +50,12 @@ def get_key(uuid): key = ac_keys.get(uuid) return key if key != None else ac_defaultkey -@simplecommand("adminchatkey", - aliases = ["ackey"], - senderLimit = 0, - helpNoargs = True, - helpSubcmd = True, - description = "Sets a key character for adminchat", +@simplecommand("adminchatkey", + aliases = ["ackey"], + senderLimit = 0, + helpNoargs = True, + helpSubcmd = True, + description = "Sets a key character for adminchat", usage = "<key>") def adminchatkey_command(sender, command, label, args): key = " ".join(args) diff --git a/adminnotes.py b/adminnotes.py index 9c6d16f..f486c7e 100644 --- a/adminnotes.py +++ b/adminnotes.py @@ -92,7 +92,7 @@ def adminnotes_command(sender, command, label, args): show_an_help(sender) return if not args[1].isdigit(): - msg(sender, "&cThe ID has to be numeric (check /an if you're unsure)") + msg(sender, "&cThe ID has to be numeric (check /an if you're unsure)") return note_id = int(args[1])-1 if note_id >= len(notes): diff --git a/basecommands.py b/basecommands.py index d2cb3ab..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 <player_to_teleport> <destination_player>. The usage is: "<player_to_teleport> <destination_player>". + 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 "<player> <msg..>" + +* 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 ")) @@ -20,13 +74,13 @@ def helpMsg(sender, cmd, description, usage, aliases, permission): def simplecommand(cmd, - aliases = [], - usage = "[args...]", + aliases = [], + usage = "[args...]", description = None, - senderLimit = -1, - amin = 0, - amax = -1, - helpNoargs = False, + senderLimit = -1, + amin = 0, + amax = -1, + helpNoargs = False, helpSubcmd = False): cmd = cmd.lower() permission = "utils." + cmd @@ -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 @@ -107,4 +161,4 @@ class Validate(): if not checkargs(sender, args, amin, amax): raise CommandException("") - + diff --git a/blockplacemods.py b/blockplacemods.py index 6885f84..85e71c5 100644 --- a/blockplacemods.py +++ b/blockplacemods.py @@ -24,7 +24,7 @@ settingInformation = dict( #[setting type, identifying description, detailed des ], furnace = [1, "automatically filling furnaces upon placement", - "Sets your preferred default furnace contents to your currently held itemstack. Use an empty hand to empty a slot, or /toggle dropper clear to clear all slots.", + "Sets your preferred default furnace contents to your currently held itemstack. Use an empty hand to empty a slot, or /toggle furnace clear to clear all slots.", ["cooker", "fillf"], 2 ], #torch = [0, @@ -44,13 +44,13 @@ settingInformation = dict( #[setting type, identifying description, detailed des ], hopper = [1, "automatically filling hoppers upon placement", - "Sets your preferred default hopper contents to your currently held itemstack. Use an empty hand to empty a slot, or /toggle dropper clear to clear all slots.", + "Sets your preferred default hopper contents to your currently held itemstack. Use an empty hand to empty a slot, or /toggle hopper clear to clear all slots.", ["itemtransporter", "fillh"], 4 ] ) defaults = { - 0: list, + 0: list, 1: dict, 2: list } @@ -87,8 +87,8 @@ def getSettingDetails(arg): raise CommandException(" &cThat setting could not be found.\n For command help, use &o/toggle &cor &o/set") @simplecommand("toggle", - aliases = ["setting", "set", "config"], - usage = "<setting> [value|info]", + aliases = ["setting", "set", "config"], + usage = "<setting> [value|info]", description = "Toggles or sets your preferences for our redstone \nutilities. The following settings are available:\n" + ", ".join([x for x in settingInformation]), senderLimit = 0, helpNoargs = True, @@ -144,13 +144,13 @@ def toggle_command(sender, command, label, args): if arg2 == "details": return " &aSetting %s:\n &9%s \n&6Accepted arguments: [<slot>|clear|details]" % (setting, details[2]) - slot = int(arg2) if arg2.isdigit() else 0 + slot = int(arg2) if arg2.isdigit() else 0 if not (0 <= slot <= details[4]): return " &cSlot number must be more than or equal to 0 and less than or equal to %s!" % details[4] item = fromStack(player.getItemInHand()) if item[0] == 0 or item[1] <= 0: - if enabled: + if enabled: items = values[uuid] if slot in items: del items[slot] @@ -197,16 +197,16 @@ def on_block_place(event): material = block.getType() - if (material in (Material.WOOD_STEP, Material.STEP) - and isEnabled("slab", uuid) - and player.hasPermission("utils.toggle.slab") + if (material in (Material.WOOD_STEP, Material.STEP) + and isEnabled("slab", uuid) + and player.hasPermission("utils.toggle.slab") and block.getData() < 8 ): block.setData(block.getData() + 8) # Flip upside down - elif (material == Material.CAULDRON - and isEnabled("cauldron", uuid) + elif (material == Material.CAULDRON + and isEnabled("cauldron", uuid) and player.hasPermission("utils.toggle.cauldron") ): block.setData(3) #3 layers of water, 3 signal strength @@ -214,7 +214,7 @@ def on_block_place(event): elif ((material == Material.FURNACE and player.hasPermission("utils.toggle.furnace")) or (material == Material.DROPPER and player.hasPermission("utils.toggle.dropper")) - or (material == Material.HOPPER and player.hasPermission("utils.toggle.hopper")) + or (material == Material.HOPPER and player.hasPermission("utils.toggle.hopper")) ): stacks = get(str(material).lower()).get(uuid) if stacks != None: # Enabled @@ -227,7 +227,7 @@ def on_block_place(event): """ elif (material == Material.REDSTONE_TORCH_ON and event.getBlockAgainst().getType() == Material.REDSTONE_BLOCK - and isEnabled("torch", uuid) + and isEnabled("torch", uuid) and player.hasPermission("utils.toggle.torch") ): torches_to_break.append(block) @@ -244,8 +244,8 @@ def on_block_place(event): @hook.event("player.PlayerInteractEvent", "monitor") def on_interact(event): player = event.getPlayer() - if (isEnabled("cauldron", uid(player)) - and player.hasPermission("utils.toggle.cauldron") + if (isEnabled("cauldron", uid(player)) + and player.hasPermission("utils.toggle.cauldron") and is_creative(player) and event.getAction() == Action.RIGHT_CLICK_BLOCK and (not event.hasItem() or event.getItem().getType() == Material.REDSTONE) @@ -269,7 +269,7 @@ def stop_breaking_torches(): class torch_breaker(Runnable): def run(): - + try: if break_torches: for i in range(len(torches_to_break)): @@ -1,29 +1,73 @@ from helpers import * +import threading +import time calc_users = open_json_file("calc", []) -math_operators = ["+", "-", "*", "/", "&", "|"] -ignore_operators = ["**", "&&", "||"] # ** may be too intensive, the others cause syntax errors +math_operators = ["+", "-", "*", "/", "%", ">", "<", "^", "&", "|"] +allowed_strings = ["0b", "0x", "(", ")", "hex(", "bin(", "abs(", "int(", "min(", "max(", "round(", "float("] +allowed_alpha = ["a", "b", "c", "d", "e", "f"] calc_perm = "utils.calc" +calc_perm_power = "utils.calc.power" +def calc(sender, text): + try: + return do_calc(sender, text.lower()) + except: + return None -def calc(text): +def do_calc(sender, text): """ extracts a mathematical expression from `text` returns (expression, result) or None """ expression = "" should_calc = False - for char in text: - if char.isdigit() or (expression and char == ".") or (should_calc and char == " "): + i = 0 + while True: + if i >= len(text): + break + char = text[i] + if i < len(text) - 5 and str(char + text[i+1] + text[i+2] + text[i+3] + text[i+4] + text[i+5]) in allowed_strings: + expression += char + expression += text[i + 1] + expression += text[i + 2] + expression += text[i + 3] + expression += text[i + 4] + expression += text[i + 5] + i += 5 + should_calc = True + elif i < len(text) - 3 and str(char + text[i+1] + text[i+2] + text[i+3]) in allowed_strings: + expression += char + expression += text[i + 1] + expression += text[i + 2] + expression += text[i + 3] + i += 3 + should_calc = True + elif i < len(text) - 1 and str(char + text[i + 1]) in allowed_strings: + expression += char + expression += text[i + 1] + i += 1 + should_calc = True + elif char.isdigit() or char in allowed_alpha or (expression and char == ".") or (should_calc and char == " ") or (should_calc and char == ","): expression += char - elif char in math_operators: + should_calc = True + elif char in math_operators or char in ["(", ")"]: # calculation must include at least 1 operator should_calc = True expression += char elif should_calc and char.isalpha(): # don't include any more text in the calculation break - if should_calc and not any(op in expression for op in ignore_operators): + i += 1 + last_char = ' ' + for char in expression: + if last_char == '*' and char == '*': + if sender.hasPermission(calc_perm_power): + break + else: + return None + last_char = char + if should_calc: try: result = str(eval(expression)) # pylint: disable = W0123 except: # pylint: disable = W0702 @@ -33,15 +77,19 @@ def calc(text): return (expression, result) return None +def thread_calc(message, sender): + output = calc(sender, message) + if output and sender.isOnline(): + msg(sender, "&2=== Calc: &e" + output[0] + " &2= &c" + output[1]) @hook.event("player.AsyncPlayerChatEvent", "monitor") def on_calc_chat(event): sender = event.getPlayer() message = event.getMessage() if not event.isCancelled() and uid(sender) in calc_users and sender.hasPermission(calc_perm): - output = calc(message) - if output: - msg(sender, "&2=== Calc: &e" + output[0] + " &2= &c" + output[1]) + thread = threading.Thread(target=thread_calc, args=(message, sender)) + thread.daemon = True + thread.start() @hook.command("calc", description="Toggles chat calculations") diff --git a/chatalias.py b/chatalias.py new file mode 100644 index 0000000..d55269c --- /dev/null +++ b/chatalias.py @@ -0,0 +1,138 @@ +# 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. The JSON file for this +# plugin is generated if not +# present. Set values to -1 +# for "unlimited" setting. + +from helpers import * +import re +from traceback import format_exc as trace + +data = None + +max_entries = 10 +max_alias_length = 35 +# Minecraft message limit is 100 so I decided to give a little tolerance (and I added a bit more) +max_overall_length = 100 + max_alias_length + +alias_perm = "utils.alias.allowed" +exceed_length = "utils.alias.exceedlimit" +exceed_entries = "utils.alias.exceedlimit" +exceed_overall_length = "utils.alias.exceedlimit" + + +def safe_open_json(): + global data + if data is not None: + return data + data = open_json_file("aliases") + if data is None: + data = {} + save_json_file("aliases", data) + return data + +def multiple_replace(aliases, text): + regex = re.compile("|".join(map(re.escape, aliases.keys()))) + return regex.sub(lambda mo: aliases[mo.group(0)], text) + + +@hook.command("alias", + usage="/<command> [to_alias] [alias...]", + desc="Aliases words in chat") +def on_alias_command(sender, cmd, label, args): + + if not is_player(sender): + msg(sender, "Sorry, non-players cannot alias words") + return True + if not sender.hasPermission(alias_perm): + plugin_header(recipient=sender, name="Chat Alias") + noperm(sender) + return True + if len(args) == 0: + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&7This is a plugin that allows you to get words" + + "replaced by other ones automatically!") + msg(sender, "&7\nCommands:") + msg(sender, "&e/alias <word> &7removes <word> from your aliases. " + + "Use &e/alias * &7to remove all aliases.") + msg(sender, "&e/alias <word> <replacement> &7will change &e<word> " + + "&7to &e<replacement> &7in chat") + msg(sender, "&7\nYour Aliases:") + data = safe_open_json() + try: + for alias, value in data[str(sender.getUniqueId())].items(): + msg(sender, "&7%s &7==> %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, "&7No alias data to remove!") + return True + save_json_file("aliases", data) + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&cALL &7alias 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, "&7Could not remove: alias not present!") + return True + except KeyError: + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&7Could not remove: you do not have any aliases!") + return True + save_json_file("aliases", data) + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&7Alias for %s &7successfuly removed" % args[0]) + return True + elif len(args) >= 2: + data = safe_open_json() + alias = " ".join(args[1:]) + try: + if (len(alias) > max_alias_length) and (max_alias_length >= 0) and (not sender.hasPermission(exceed_length)): + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&7Please do not alias long words/sentences.") + return True + if (len(data[str(sender.getUniqueId())]) >= max_entries) and (max_entries >= 0) and (not sender.hasPermission(exceed_entries)): + plugin_header(recipient=sender, name="Chat Alias") + msg(sender, "&7You have reached your alias limit!") + 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, "&7Chat Alias %s &7==> %s &7successfully 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 + if not data[playerid]: + return + event.setMessage(multiple_replace(data[playerid], event.getMessage())) + + if (event.getPlayer().hasPermission("essentials.chat.color")): + event.setMessage(colorify(event.getMessage())) + if (max_overall_length >= 0) and (len(event.getMessage()) > max_overall_length) and (not event.getPlayer().hasPermission(exceed_overall_length)): + event.setCancelled(True) + plugin_header(recipient=event.getPlayer(), name="Chat Alias") + msg(event.getPlayer(), "&7The message generated was too long and was not sent. :/") + diff --git a/chatgroups.py b/chatgroups.py index 9e85150..9965b96 100644 --- a/chatgroups.py +++ b/chatgroups.py @@ -43,11 +43,18 @@ def on_chatgroup_command(sender, command, label, args): msg(sender, "&aUse chat like '&e%s<message>' to send messages to this group." % get_key(sender_id)) elif len(args) == 1 and args[0] == "key": msg(sender, "&aYour chatgroup key is currently: '&c%s&a'" % get_key(sender_id)) + elif len(args) == 1 and args[0] == "tpahere": + if sender_id in groups.keys(): + do_for_chatgroup(groups[sender_id], send_tpa_request, sender) + msg(sender, "&aSent a tpahere request to all users in your chatgroup") + else: + msg(sender, "&cYou have to be in a chatgroup to do that") else: msg(sender, "&e/chatgroup join <name>") msg(sender, "&e/chatgroup leave") msg(sender, "&e/chatgroup info") msg(sender, "&e/chatgroup key") + msg(sender, "&e/chatgroup tpahere") @hook.command("cgt") @@ -72,11 +79,19 @@ def groupchat(sender, message, ann = False): mesg = "&8[&bCG&8] &e&o%s&e&o %s" % (name, message) else: mesg = "&8[&bCG&8] &f%s&f: &6%s" % (name, message) + mesg = colorify(mesg) + info("[ChatGroups] %s (%s): %s" % (sender.getDisplayName(), group, message)) - for receiver in server.getOnlinePlayers(): - groups.get(uid(receiver)) == group and msg(receiver, mesg) + do_for_chatgroup(group, msg, mesg, usecolor = False) +def do_for_chatgroup(group, func, *args, **kwargs): + for receiver in server.getOnlinePlayers(): + if groups.get(uid(receiver)) == group: + func(receiver, args, kwargs) +def send_tpa_request(receiver, sender): + if not receiver == sender: + runas(sender, "/tpahere " + receiver.getName()) def save_groups(): save_json_file("chatgroups", groups) @@ -97,12 +112,12 @@ def on_chat(event): groupchat(sender, msge) event.setCancelled(True) -@simplecommand("chatgroupkey", - aliases = ["cgkey"], - senderLimit = 0, - helpNoargs = True, - helpSubcmd = True, - description = "Sets a key character for chatting to your chatgroup", +@simplecommand("chatgroupkey", + aliases = ["cgkey"], + senderLimit = 0, + helpNoargs = True, + helpSubcmd = True, + description = "Sets a key character for chatting to your chatgroup", usage = "<key>") def chatgroupkey_command(sender, command, label, args): key = " ".join(args) @@ -115,5 +130,6 @@ def chatgroupkey_command(sender, command, label, args): save_keys() return "&aYour chatgroup key was set to: '&c%s&a'" % key + def save_keys(): save_json_file("chatgroup_keys", cg_keys) @@ -86,7 +86,10 @@ def on_hook_command(sender, command, label, args): plugin_header(sender, "Check") msg(sender, "&7Please notice that the data may not be fully accurate!") player = server.getOfflinePlayer(args[0]) if len(args) > 0 else None - get_all_data(sender, player) + + t = threading.Thread(target=get_all_data, args=(sender, player)) + t.daemon = True + t.start() else: msg(sender, "&4You don't have the required permissions to execute this command!") return True
\ No newline at end of file @@ -1,19 +1,30 @@ #pylint: disable = F0401 -from re import sub -from java.util.UUID import fromString as juuid -from json import dumps as json_dumps, loads as json_loads + 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.block as bblock +import org.bukkit.Location as Location import org.bukkit.event.entity as entity +import org.bukkit.entity.Player as Player import org.bukkit.command.ConsoleCommandSender +import org.bukkit.event.block.BlockBreakEvent as BlockBreakEvent +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause as TeleportCause + +from re import sub +from thread_utils import * +from player import py_players from org.bukkit.entity import * from player import get_py_player -from player import py_players - from traceback import format_exc as trace +from java.util.UUID import fromString as juuid +from json import dumps as json_dumps, loads as json_loads + +#Imports for async query +import threading +import mysqlhack + +from secrets import * +from com.ziclix.python.sql import zxJDBC + shared = {} # this dict can be used to share stuff across modules @@ -80,7 +91,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): @@ -128,7 +139,7 @@ def runas(player, cmd): player.chat("/" + cmd) else: server.dispatchCommand(player, cmd) - + def is_player(obj): """ @@ -137,6 +148,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 @@ -165,6 +185,16 @@ def is_creative(player): return str(player.getGameMode()) == "CREATIVE" +def is_rank(player, rank): + """ + rank: a string equal to the PEX group name found in /pex groups + returns True if one of the following conditions are met: + - the player is of the given rank, + - their rank inherits the given rank. + """ + return player.hasPermission("groups." + rank) + + def uid(player): """ returns the player's UUID @@ -188,7 +218,6 @@ def known_player(player): """ return player.hasPlayedBefore() - def open_json_file(filename, default = None): """ opens the given json file and returns an object or returns None on error @@ -225,4 +254,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 = "/<command> <on|off|status>", + 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..8bbd053 --- /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/loginsecurity.py b/loginsecurity.py index e96edf2..89f9e47 100644 --- a/loginsecurity.py +++ b/loginsecurity.py @@ -37,10 +37,10 @@ def matches_thread(password, user): @simplecommand("cgpass", - usage = "<password> <new password>", - description = "Changes your password", - senderLimit = 0, - helpNoargs = True) + usage = "<password> <new password>", + description = "Changes your password", + senderLimit = 0, + helpNoargs = True) def change_pass_command(sender, command, label, args): py_player = get_py_player(sender) @@ -63,9 +63,9 @@ def change_pass_command(sender, command, label, args): @simplecommand("login", - usage = "<password>", + usage = "<password>", description = "Logs you in if <password> matches your password.", - senderLimit = 0, + senderLimit = 0, helpNoargs = True) def login_command(sender, command, label, args): py_player = get_py_player(sender) @@ -203,12 +203,12 @@ def delete_pass(uuid): curs.close() conn.close() -@hook.event("player.PlayerJoinEvent", "low") +@hook.event("player.PlayerJoinEvent", "highest") def on_join(event): user = event.getPlayer() py_player = get_py_player(event.getPlayer()) if is_registered(uid(user)): - msg(event.getPlayer(), "&6You will be disconnected after 60 seconds if you don't &alogin") + msg(event.getPlayer(), "&4You will be disconnected after 60 seconds if you don't &alogin") msg(user, "&cUse /login <password>") py_player.logging_in = True py_player.login_time = time.time() @@ -264,3 +264,12 @@ for blocked_event in blocked_events: user = get_py_player(event.getPlayer()) if user.logging_in: event.setCancelled(True) + +@hook.event("player.PlayerCommandPreprocessEvent","normal") +def pre_command_proccess(event): + player = get_py_player(event.getPlayer()) + if player.logging_in: + args = event.getMessage().split(" ") + if not args[0].lower() == "/login": + msg(player.player, "&4You need to login before you do that!") + event.setCancelled(True) @@ -11,8 +11,9 @@ sys.path += ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/l try: # Library that adds a bunch of re-usable methods which are used in nearly all other modules from helpers import * + from wrapper import * except: - print("[RedstonerUtils] ERROR: Failed to import helpers:") + print("[RedstonerUtils] ERROR: Failed to import Wrapper:") print(print_traceback()) @@ -24,63 +25,17 @@ def on_enable(): @hook.disable def on_disable(): - shared["modules"]["reports"].stop_reporting() + #shared["modules"]["reports"].stop_reporting() info("RedstonerUtils disabled!") info("Loading RedstonerUtils...") + + # Import all modules, in this order -shared["load_modules"] = [ - # Collection of tiny utilities - "misc", - # Adds chat for staff using /ac <text or ,<text> - "adminchat", - # Adds /badge, allows to give players achievements - "badges", - # Adds a few block placement corrections/mods - "blockplacemods", - # Adds /calc, toggles automatic solving of Math expressions in chat - "calc", - # Plugin to locate laggy chunks. /lc <n> lists chunks with more than n entities - "lagchunks", - # Adds /report and /rp, Stores reports with time and location - "reports", - # Adds group-chat with /chatgroup and /cgt to toggle normal chat into group mode - "chatgroups", - # Adds /token, reads and writes from the database to generate pronouncable (and thus memorable) registration-tokens for the website - "webtoken", - # Adds /lol, broadcasts random funyy messages. A bit like the splash text in the menu - "saylol", - # Shows the owner of a skull when right-clicked - "skullclick", - # Adds /listen, highlights chat and plays a sound when your name was mentioned - "mentio", - # Adds /cycler, swaps the hotbar with inventory when player changes slot from right->left or left->right - "cycle", - # Adds /getmotd & /setmotd to update the motd on the fly (no reboot) - "motd", - # AnswerBot. Hides stupid questions from chat and tells the sender about /faq or the like - "abot", - # Adds '/forcefield', creates forcefield for players who want it. - "forcefield", - # Adds /damnspam, creates timeout for buttons/levers to mitigate button spam. - "damnspam", - # Adds /check, useful to lookup details about a player - "check", - # Adds /an, a command you can use to share thoughts/plans/news - "adminnotes", - # Adds /imout, displays fake leave/join messages - "imout", - #adds snowbrawl minigame - "snowbrawl", - # Adds /tm [player] for a messages to be sent to this player via /msg - "pmtoggle", - # Replacement for LoginSecurity - "loginsecurity", - # Centralized Player class - "player" -] +shared["load_modules"] = ["test"] + shared["modules"] = {} for module in shared["load_modules"]: try: @@ -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 <word>" % 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 @@ -6,7 +6,8 @@ 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 +import java.util.Arrays as Arrays @@ -70,7 +71,6 @@ def cmd_event2(event): - """ Disabled while builder can't access Trusted @hook.event("player.PlayerGameModeChangeEvent", "low") def on_gamemode(event): @@ -114,8 +114,9 @@ def rs_material_broken_by_flow(material): length = len(parts) return length > 1 and (parts[0] == "DIODE" or parts[1] in ("TORCH", "WIRE", "BUTTON", "HOOK") or (length == 3 and parts[1] == "COMPARATOR")) - - +sudo_blacklist = ["pyeval", "script_backup_begin", "script_backup_end", "script_backup_error", "script_backup_database_begin", "script_backup_database_dumps", "script_backup_database_end", +"script_backup_database_error", "script_backup_database_abort", "script_trim", "script_trim_result", "script_spigot_update", "script_disk_filled", "script_restart", "script_restart_abort", +"script_stop", "script_stop_abort", "script_shutdown", "stop", "esudo", "essentials:sudo"] @simplecommand("sudo", usage = "<player> [cmd..]", @@ -128,6 +129,9 @@ def on_sudo_command(sender, command, label, args): msg(sender, "&2[SUDO] &rRunning '&e%s&r' as &3%s" % (cmd, target)) is_cmd = cmd[0] == "/" is_console = target.lower() in ["server", "console"] + first_cmd = (args[1])[1:] if is_cmd else None + if first_cmd in sudo_blacklist and (is_player(sender) and uid(sender) not in pythoners): + return "&cYou can't sudo this command" if is_console: server.dispatchCommand(server.getConsoleSender(), cmd[1:] if is_cmd else cmd) return None @@ -139,7 +143,7 @@ def on_sudo_command(sender, command, label, args): -@simplecommand("me", +@simplecommand("me", usage = "[message..]", description = "Sends a message in third person", helpNoargs = True) @@ -158,7 +162,8 @@ def on_pluginversions_command(sender, command, label, args): """ try: plugin_header(sender, "Plugin versions") - plugins = [pl.getDescription() for pl in list(ArrayList(java_array_to_list(server.getPluginManager().getPlugins())))] + raw_plugins = server.getPluginManager().getPlugins() # Plugin[] + plugins = [raw_plugins[i].getDescription() for i in range(len(raw_plugins))] info(type(plugins[0]).__name__) plugins.sort(key = lambda pl: pl.getDescription().getName()) msg(sender, "&3Listing all " + str(len(plugins)) + " plugins and their version:") @@ -196,11 +201,20 @@ def eval_thread(sender, code): msg(sender, ">>> %s: %s" % (eclass.__name__, e) + "\n ", False, "c") thread.exit() +pythoners = [ +"e452e012-2c82-456d-853b-3ac8e6b581f5", # Nemes +"ae795aa8-6327-408e-92ab-25c8a59f3ba1", # jomo +"305ccbd7-0589-403e-a45b-d791dcfdee7d", # PanFritz +"51f2ad3c-6cc8-40ea-aa2b-f25970316921" # Dico +] + @simplecommand("pyeval", usage = "[code..]", description = "Runs python [code..] and returns the result", helpNoargs = True) def on_pyeval_command(sender, command, label, args): + if is_player(sender) and uid(sender) not in pythoners: + return noperm(sender) msg(sender, " ".join(args), False, "e") thread.start_new_thread(eval_thread, (sender, " ".join(args))) return None @@ -245,6 +259,31 @@ def on_modules_command(sender, command, label, args): msg(sender, ", ".join([(("&a" if mod in shared["modules"] else "&c") + mod) for mod in shared["load_modules"]])) +@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): + if sender.hasPermission("utils.warn"): + broadcast(None, " &b= &2&lLag incoming! &r-%s" % sender.getDisplayName()) + else: + noperm(sender) + + +@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): + if sender.hasPermission("utils.warnp"): + broadcast(None, " &b= &2&lPossible lag incoming! &r-%s" % sender.getDisplayName()) + else: + noperm(sender) + + + """ Something I'm planning for schematics @hook.event("player.PlayerCommandPreprocessEvent", "low") def on_command(event): diff --git a/mysql_utils.py b/mysql_utils.py new file mode 100644 index 0000000..e59c3d9 --- /dev/null +++ b/mysql_utils.py @@ -0,0 +1,33 @@ +import mysqlhack +from secrets import * +from thread_utils import * +from com.ziclix.python.sql import zxJDBC +from traceback import format_exc as trace + +class mysql_connect: + def __init__(self): + self.conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") + self.curs = self.conn.cursor() + + def execute(self, query, args=None): + if args is None: + return self.curs.execute(query) + else: + return self.curs.execute(query, args) + + def fetchall(self): + return self.curs.fetchall() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_inst, exc_tb): + if exc_type is None: + try: + self.conn.commit() + self.curs.close() + self.conn.close() + except: + print(trace()) + else: + print(exc_tb) diff --git a/mysqlhack.py b/mysqlhack.py index 7153bdf..5d45c52 100644 --- a/mysqlhack.py +++ b/mysqlhack.py @@ -4,12 +4,12 @@ A library that makes use of the so called ClassPathHack for jython to allow proper loading of mysql-connector.jar at runtime. Import only, no methods. """ -import java.lang.reflect.Method -import java.io.File -import java.net.URL -import java.net.URLClassLoader import jarray +import java.net.URL +import java.io.File from java.lang import Class +import java.net.URLClassLoader +import java.lang.reflect.Method # hacky code to add mysql-connector to java's classpath ('classPathHack') @@ -26,3 +26,4 @@ def on_join(event): @hook.event("player.PlayerQuitEvent","highest") def on_leave(event): py_players.remove(get_py_player(event.getPlayer())) + diff --git a/punishments.py b/punishments.py new file mode 100644 index 0000000..4b57be1 --- /dev/null +++ b/punishments.py @@ -0,0 +1,129 @@ +from helpers import * +from java.util.UUID import fromString as juuid +import org.bukkit.Material as Material + +spawn_world = "Spawn" +punish_world = "Punishments" + +slave_perm = "utils.minerslaves" + +slaves = [] + +def save_slaves(): + buf = [] + for slave in slaves: + buf.append(slave.get_data()) + save_json_file("miner_slaves", buf) + +def load_slaves(): + buf = open_json_file("miner_slaves", []) + for data in buf: + slave = Slave(True, None, None) + slave.load_data(data) + slaves.append(slave) + +def get_slave(player): + for slave in slaves: + if slave.get_uuid() == player: + return slave + return None + +class Slave(object): + + def __init__(self, from_file, player, amount): + if from_file: + self.players = None + self.blocks = None + return + slave = get_slave(uid(player)) + if slave != None: + slave.set_blocks(slave.get_blocks() + amount) + else: + self.player = uid(player) + self.blocks = amount + slaves.append(self) + + def get_uuid(self): + return self.player + + def get_blocks(self): + return self.blocks + + def set_blocks(self, amount): + self.blocks = amount + + def update(self): + self.blocks -= 1 + if self.blocks <= 0: + server.getPlayer(juuid(self.get_uuid())).teleport(server.getWorld(spawn_world).getSpawnLocation()) + slaves.remove(self) + save_slaves() + + def get_data(self): + return { + "player": self.player, + "amount": self.blocks + } + + def load_data(self, data): + self.player = str(data["player"]) + self.blocks = int(data["amount"]) + +load_slaves() + +@hook.event("block.BlockBreakEvent", "low") +def event(event): + if event.getPlayer().getWorld().getName() != punish_world: + return + slave = get_slave(uid(event.getPlayer())) + if slave != None and event.getBlock().getType() == Material.OBSIDIAN: + slave.update() + +@hook.command("miner") +def command(sender, cmd, label, args): + if not sender.hasPermission(slave_perm): + noperm(sender) + return True + if len(args) == 0 or (len(args) != 1 and args[0] == "list") or (len(args) != 2 and args[0] == "rem") or (len(args) != 3 and args[0] == "add"): + msg(sender, "&e-&a /miner add/rem/list <name> <amount>") + return True + if args[0] == "add": + try: + int(args[2]) + except: + msg(sender, "&cArgument <amount> is not a number") + return True + if args[0] == "list": + if len(slaves) == 0: + msg(sender, "&e-&a There are no people mining obsidian") + return True + for slave in slaves: + 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])) + if player.isOnline(): + 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") + return True + elif args[0] == "rem": + player = server.getOfflinePlayer(str(args[1])) + if player.isOnline(): + slave = get_slave(uid(player)) + if slave != None: + server.getPlayer(juuid(slave.get_uuid())).teleport(server.getWorld(spawn_world).getSpawnLocation()) + slaves.remove(slave) + save_slaves() + else: + msg(sender, "&e-&a Player not in punishments") + else: + msg(sender, "&cYou can only remove online players") + return True + else: + msg(sender, "&e-&a /miner add/rem/list <name> <amount>") + return True diff --git a/scriptutils.py b/scriptutils.py new file mode 100644 index 0000000..312f6c4 --- /dev/null +++ b/scriptutils.py @@ -0,0 +1,212 @@ +from helpers import * + +""" +Prints server restart message +arg 0 timeout +arg 1 $(whoami) +arg 2: reason +""" +@hook.command("script_restart") +def print_restart(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&2&l=============================================") + broadcast(None, "&r") + broadcast(None, "&r") + broadcast(None, "&9%s is restarting the server." % args[1]) + broadcast(None, "&a&lServer is going to restart in %s seconds." % args[0]) + broadcast(None, "&6&l%s" % " ".join(args[2:])) + broadcast(None, "&r") + broadcast(None, "&r") + broadcast(None, "&2&l=============================================") + else: + noperm(sender) + +""" +Prints the server shut down message +arg 0 timeout +arg 1 $(whoami) +arg 2: reason +""" +@hook.command("script_stop") +def print_stop(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&2&l=============================================") + broadcast(None, "&r") + broadcast(None, "&r") + broadcast(None, "&9%s is shutting down the server." % args[1]) + broadcast(None, "&a&lServer is going to shut down in %s seconds." % args[0]) + broadcast(None, "&6&l%s" % " ".join(args[2:])) + broadcast(None, "&r") + broadcast(None, "&r") + broadcast(None, "&2&l=============================================") + else: + noperm(sender) + +""" +Prints the shut down abort message +""" +@hook.command("script_stop_abort") +def abort_stop(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4&oShut down has been aborted.") + else: + noperm(sender) + +""" +Prints the restart abort message +""" +@hook.command("script_restart_abort") +def abort_restart(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4&oRestart has been aborted.") + else: + noperm(sender) + +""" +Prints the backup started message, saves all worlds and turns off world saving +""" +@hook.command("script_backup_begin") +def print_backup_begin(sender, command, lable, args): + if not is_player(sender): + broadcast(None, "&4 =&2 Starting backup now.") + server.dispatchCommand(server.getConsoleSender(), "save-all") + server.dispatchCommand(server.getConsoleSender(), "save-off") + else: + noperm(sender) + +""" +Prints the backup finished message and turns on world saving +""" +@hook.command("script_backup_end") +def print_backup_end(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4 =&2 Backup completed.") + server.dispatchCommand(server.getConsoleSender(), "save-on") + else: + noperm(sender) + +""" +Prints the backup error message and turns on world saving +""" +@hook.command("script_backup_error") +def print_backup_error(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4 =&c&l Error while backing up!") + server.dispatchCommand(server.getConsoleSender(), "save-on") + else: + noperm(sender) + +""" +Prints the world trimming started message and starts trimming +""" +@hook.command("script_trim") +def print_backup_trim(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4 =&3 Deleting all chunks beyond border now.") + server.dispatchCommand(server.getConsoleSender(), "wb Creative trim 1000000 15") + server.dispatchCommand(server.getConsoleSender(), "wb trim confirm") + else: + noperm(sender) + +""" +Prints the thimming finished message +arg 0 size difference of world +arg 1: world border trim data +""" +@hook.command("script_trim_result") +def print_backup_trim_res(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&4 =&3 Chunk deletion saved %s (&a%sMB&3)" % (" ".join(args[1:]), args[0])) + else: + noperm(sender) + +""" +Prints the database backup started message and admin-chat warning +""" +@hook.command("script_backup_database_begin") +def print_backup_db_begin(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&6 =&2 Starting database backup now.") + server.dispatchCommand(server.getConsoleSender(), "ac &aLogblock may be unavailable!") + else: + noperm(sender) + +""" +Prints the database dumps compression started message +""" +@hook.command("script_backup_database_dumps") +def print_backup_db_dumps(sender, command, label, args): + if not is_player(sender): + server.dispatchCommand(server.getConsoleSender(), "ac &aDumps completed, logblock available again.") + server.dispatchCommand(server.getConsoleSender(), "ac &aNow compressing dumps, will take a while...") + else: + noperm(sender) + +""" +Prints the database finished message and backup size in admin-chat +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 Database backup completed.") + server.dispatchCommand(server.getConsoleSender(), "ac &abackup size: &2%sMB&a." % args[0]) + else: + noperm(sender) + +""" +Prints the database backup error message +""" +@hook.command("script_backup_database_error") +def print_backup_db_error(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&6 =&c&l Error while backing up database!") + else: + noperm(sender) + +""" +Prints the database backup abort message +""" +@hook.command("script_backup_database_abort") +def print_backup_db_abort(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&6 =&2 Database backup aborted.") + else: + noperm(sender) + +""" +Prints the spigot update message +""" +@hook.command("script_spigot_update") +def print_update(sender, command, label, args): + if not is_player(sender): + broadcast(None, "&9 =&2 A new Spigot version has been downloaded!") + broadcast(None, "&9 =&2 Update will be applied after the next reboot.") + else: + noperm(sender) + +""" +Prints the admin-chat warning for disk is filled +arg 0 fill percentage +""" +@hook.command("script_disk_filled") +def print_disk_filled(sender, command, label, args): + if not is_player(sender): + server.dispatchCommand(server.getConsoleSender(), "ac &4&lWARNING:&6 Disk is filled > 96% (" + args[0] + "%)") + server.dispatchCommand(server.getConsoleSender(), "ac &4 Server will shut down at 98%!") + server.dispatchCommand(server.getConsoleSender(), "ac &4 Contact an admin &nimmediately&4!") + else: + noperm(sender) + +""" +Saves all worlds, kicks players and shuts down the server +arg 0: reason +""" +@hook.command("script_shutdown") +def shutdown(sender, command, label, args): + if not is_player(sender): + server.dispatchCommand(server.getConsoleSender(), "save-all") + server.dispatchCommand(server.getConsoleSender(), "kickall %s" % " ".join(args)) + server.dispatchCommand(server.getConsoleSender(), "stop") + else: + noperm(sender) diff --git a/servercontrol.py b/servercontrol.py new file mode 100644 index 0000000..91055d7 --- /dev/null +++ b/servercontrol.py @@ -0,0 +1,115 @@ +from helpers import * +import socket +import threading +import time +from java.lang import Runnable +from adminchat import adminchat + +""" +Module to allow our servercontrol telnet server forward chat and speak in AC + + +""" +host = "" +port = 1122 + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + +try: + sock.bind((host,port)) + sock.setblocking(True) + sock.listen(5) +except socket.error as e: + print(str(e)) + +def command_process(text): + text = list(text) + args = [] + arg = "" + + for char in text: + if char != " " and char != "\n" and char != "\r" and char != "\t": + arg += char + elif arg != "": + args.append(arg) + arg = "" + if arg != "": + args.append(arg) + return args + +clients = [] +clients_l = threading.Lock() + +class client(): + def __init__(self,conn,address,name): + self.conn = conn + self.address = address + self.name = name + + with clients_l: + clients.append(self) + + self.conn.setblocking(False) + + self.client_thread = threading.Thread(target=self.client_t) + self.client_thread.daemon = True + self.client_thread.start() + + def getName(self): + return self.name + + + def close_connection(self): + try: + self.conn.close() + with clients_l: + clients.remove(self) + except: + pass + + + def client_t(self): + + while True: + time.sleep(0.1) + try: + data = self.conn.recv(1024) + except: + if self not in clients: + self.close_connection() + continue + + if self not in clients: #If the connection was closed, kill the thread + break + + adminchat(self,data) + + +def handle_conn(): + while True: + try: + conn, address = sock.accept() + except: + time.sleep(0.1) + continue + + #Send name + data = conn.recv(1024) + data = command_process(data) + print "servercontrol connected! %s " %data[0] + client_c = client(conn, address,data[0]) + + +handle_conn_t = threading.Thread(target=handle_conn) +handle_conn_t.daemon = True +handle_conn_t.start() + + +@hook.event("player.AsyncPlayerChatEvent","low") +def on_chat(event): + sender = event.getPlayer().getName() + msg = event.getMessage() + + for entry in clients: + entry.conn.sendall(sender + " " + msg)
\ No newline at end of file diff --git a/serversigns.py b/serversigns.py new file mode 100644 index 0000000..297b727 --- /dev/null +++ b/serversigns.py @@ -0,0 +1,297 @@ +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 + +blocked_cmds = ("pex", "kick", "ban", "tempban", "pyeval", "sudo", "stop", "reload", "op", "deop", "whitelist") + +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) + +signs = load_signs() # {("world", x, y, z): ["owner_id", "msg1", "msg2"]} + +lines = {} # Accumulated messages so players can have longer messages: {"Dico200": "Message...........", ""} + +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 + + +@simplecommand(cmd = "serversigns", aliases = ["svs", "signmsg"], + description = "Makes something happen when you right click signs. \nUse /svs help for more details.", + usage = "<claim|reset|add <msg>[++]|remove <ID>|clear|info|help>", + helpNoargs = True, + senderLimit = 0) +def svs_command(sender, command, label, args): + arg1 = args[0].lower() + Validate.isTrue(arg1 in ("claim", "reset", "add", "remove", "info", "clear", "help", "switch", "reverse", "unclaim"), + signsMsg("That argument could not be recognized, use &o/svs help &4for expected arguments")) + Validate.isAuthorized(sender, "utils.serversigns." + arg1) + + #-------------------- Sub commands that don't require any conditions ----------------------- + if arg1 == "help": + admin = sender.hasPermission("utils.serversigns.admin") + msg = signsMsg("Server signs lets you add messages to a sign.", 'a') + msg += "\nRight clicking the sign will display all the messages. Commands" + msg += "\ncan also be added, by prefixing the message with a '/'." + msg += "\nHow to use &b/serversigns&a:" + msg += "\n&b/svs claim" + ("" if not sender.hasPermission("utils.serversigns.admin") else " [owner]") + msg += "\n&a- Claims the sign so that you can add messages to it" + msg += "\n&b/svs info" + msg += "\n&a- Displays information about the (claimed) sign" + msg += "\n&b/svs add <message>[++]" + msg += "\n&a- Adds the message to the sign. Use ++ at the end" + msg += "\n&a- to add the message to your buffer. You can then use" + msg += "\n&a- the same command again to create a longer message." + msg += "\n&b/svs remove <message ID>" + msg += "\n&a- Removes the message with the given ID from the sign." + msg += "\n&a- The ID is given before each message by &b/svs info&a." + msg += "\n&b/svs switch|reverse <message ID 1> <message ID 2>" + msg += "\n&a- Reverses the order in which the given messages are shown." + msg += "\n&b/svs clear" + msg += "\n&a- Removes all messages from the sign." + msg += "\n&b/svs reset|unclaim" + msg += "\n&a- Resets the sign, removing all messages and its owner." + return msg + #------------------------------------------------------------------------------------------- + + 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 arg1 == "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 arg1 == "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 arg1 == "add": + 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] = "" + 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(line[0] != "/" or line.split(" ")[0][1:] not in blocked_cmds, signsMsg("Usage of that command with server signs is prohibited")) + 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 arg1 == "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 arg1 in ("switch", "reverse"): + 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 arg1 == "clear": + signs[loc] = [sign[0]] + save_signs() + return signsMsg("Removed all messages from the %s" % signName, 'a') + + + if arg1 in ("reset", "unclaim"): + 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): + try: + global checking_block + 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) + except: + error(trace()) + + +def check_sign(event, block, attached = True): + player = event.getPlayer() + sign = getSign(fromLoc(block.getLocation())) + 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: + loc = fromLoc(block.getLocation()) + 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() + diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..336376f --- /dev/null +++ b/setup.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env bash + +# exit on failure +set -e + +for cmd in curl java unzip git pip; do + if ! which "$cmd" >/dev/null; then + tput setf 4 >&2 + echo "Error: please install '$cmd' to proceed" >&2 + tput sgr0 >&2 + exit 1 + fi +done + +echo -e "> This will only set up Spigot and all the plugins, configuration files are still up to you to manage" +echo -e "> Press enter to coninue" +read + +mkdir -v "redstoner" +cd "redstoner" + +mkdir -v "server" + +mkdir -v "build" +cd "build" + +echo -e "\n> Downloading Spigot build tools" +curl --progress-bar -Lo "buildtools.jar" "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" + +echo -e "\n> Building Spigot, this will take a while ..." +java -jar BuildTools.jar > /dev/null + +cp -v spigot-*.jar "../server/spigot.jar" +rm spigot-*.jar +cd "../server/" + +mkdir -v "plugins" +cd "plugins" + +echo ">> Downloading essentials.jar ..." +curl --progress-bar -Lo "essentials.jar" "https://github.com/RedstonerServer/Essentials/releases/download/stable-2.9.6-REDSTONER/Essentials-2.x-REDSTONER.jar" +echo ">> Downloading essentialschat.jar ..." +curl --progress-bar -Lo "essentialschat.jar" "https://hub.spigotmc.org/jenkins/job/Spigot-Essentials/lastSuccessfulBuild/artifact/EssentialsChat/target/EssentialsChat-2.x-SNAPSHOT.jar" +echo ">> Downloading imageonmap.jar ..." +curl --progress-bar -Lo "imageonmap.jar." "https://dev.bukkit.org/media/files/772/680/imageonmap.jar" +echo ">> Downloading logblock.jar ..." +curl --progress-bar -Lo "logblock.jar." "https://dev.bukkit.org/media/files/757/963/LogBlock.jar" +echo ">> Downloading logblockquestioner.zip ..." +curl --progress-bar -Lo "logblockquestioner.zip" "https://cloud.github.com/downloads/DiddiZ/LogBlockQuestioner/LogBlockQuestioner%20v0.03.zip" +echo ">> Downloading multiverse-core.jar ..." +curl --progress-bar -Lo "multiverse-core.jar" "https://dev.bukkit.org/media/files/588/781/Multiverse-Core-2.4.jar" +echo ">> Downloading multiverse-portals.jar ..." +curl --progress-bar -Lo "multiverse-portals.jar." "https://dev.bukkit.org/media/files/588/790/Multiverse-Portals-2.4.jar" +echo ">> Downloading multiverse-netherportals.jar ..." +curl --progress-bar -Lo "multiverse-netherportals.jar." "https://dev.bukkit.org/media/files/589/64/Multiverse-NetherPortals-2.4.jar" +echo ">> Downloading multiverse-inventories.jar ..." +curl --progress-bar -Lo "multiverse-inventories.jar." "https://dev.bukkit.org/media/files/663/303/Multiverse-Inventories-2.5.jar" +echo ">> Downloading permissionsex.jar ..." +curl --progress-bar -Lo "permissionsex.jar" "https://dev.bukkit.org/media/files/882/992/PermissionsEx-1.23.3.jar" +echo ">> Downloading plotme.jar ..." +curl --progress-bar -Lo "plotme.jar" "http://ci.worldcretornica.com/job/PlotMe-Core/244/artifact/target/PlotMe-Core.jar" +echo ">> Downloading plotme-defaultgenerator.jar ..." +curl --progress-bar -Lo "plotme-defaultgenerator.jar" "http://ci.worldcretornica.com/job/PlotMe-DefaultGenerator/83/artifact/target/PlotMe-DefaultGenerator.jar" +echo ">> Downloading serversigns.jar ..." +curl --progress-bar -Lo "serversigns.jar." "https://dev.bukkit.org/media/files/876/381/ServerSigns.jar" +echo ">> Downloading redstoneclockdetector.jar ..." +curl --progress-bar -Lo "redstoneclockdetector.jar." "https://dev.bukkit.org/media/files/577/253/RedstoneClockDetector.jar" +echo ">> Downloading vault.jar ..." +curl --progress-bar -Lo "vault.jar" "https://dev.bukkit.org/media/files/837/976/Vault.jar" +echo ">> Downloading worldborder.jar ..." +curl --progress-bar -Lo "worldborder.jar." "https://dev.bukkit.org/media/files/883/629/WorldBorder.jar" +echo ">> Downloading worldguard.jar ..." +curl --progress-bar -Lo "worldguard.jar." "https://github.com/RedstonerServer/WorldGuard/releases/download/6.0.0-redstoner/worldguard-6.0.0-REDSTONER.jar" +echo ">> Downloading worldedit.jar ..." +curl --progress-bar -Lo "worldedit.jar" "https://dev.bukkit.org/media/files/880/435/worldedit-bukkit-6.1.jar" +echo ">> Downloading pythonpluginloader.jar ..." +curl --progress-bar -Lo "pythonpluginloader.jar" "https://bamboo.gserv.me/browse/PLUG-PYPL/latestSuccessful/artifact/JOB1/Version-agnostic-jar/PyPluginLoader.jar" + +echo -e "\n> Unpacking LogBlockQuestioner" +unzip -q "logblockquestioner.zip" "LogBlockQuestioner.jar" +rm "logblockquestioner.zip" +mv -v "LogBlockQuestioner.jar" "logblockquestioner.jar." + +echo -e "\n> Pulling redstoner-utils ..." +git clone -q "git@github.com:RedstonerServer/redstoner-utils.git" "redstoner-utils.py.dir" > /dev/null + +echo -e "\n> Installing dependencies" +pip install passlib + +echo -e "\n> All plugins downloaded" + +cd "redstoner-utils.py.dir" +echo -e "\n> Duplicating sample files" +for file in ls ./*.example; do + cp -v "$file" "$(echo "$file" | rev | cut -d "." -f 2- | rev)" +done + +cd "../.." + +mkdir -v "lib" +cd "lib" + +echo -e "\n> Downloading MySQL Connector ..." +curl --progress-bar -Lo "mysql-connector.zip" "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.36.zip" +echo "> Extracting MySQL Connector" +unzip -p mysql-connector.zip "mysql-connector-java-5.1.36/mysql-connector-java-5.1.36-bin.jar" > mysql-connector.jar +rm "mysql-connector.zip" + +cd ".." + +echo -e "\n> Creating startup script" + +cat > start.sh <<EOF +#!/usr/bin/env bash +java -Xms512m -Xmx1g -jar spigot.jar +EOF +chmod +x start.sh + +port="25565" +re='^[0-9]+$' +if [[ "$1" =~ $re ]]; then + port="$1" +fi + +echo "> Setting port to $port" +echo "> Generating server.properties" + +cat > server.properties <<EOF +#Minecraft server properties +#Sat Jul 25 15:42:21 CEST 2015 +spawn-protection=16 +generator-settings= +force-gamemode=false +allow-nether=true +gamemode=1 +broadcast-console-to-ops=true +enable-query=false +player-idle-timeout=0 +difficulty=1 +spawn-monsters=true +op-permission-level=4 +resource-pack-hash= +announce-player-achievements=true +pvp=true +snooper-enabled=true +level-type=FLAT +hardcore=false +enable-command-block=false +max-players=20 +network-compression-threshold=256 +max-world-size=29999984 +server-port=$port +server-ip= +spawn-npcs=true +allow-flight=false +level-name=world +view-distance=10 +resource-pack= +spawn-animals=true +white-list=false +generate-structures=true +online-mode=true +max-build-height=256 +level-seed= +use-native-transport=true +motd=Redstoner dev server +enable-rcon=false +EOF + +echo -e "> Generating eula.txt" +echo "eula=true" > eula.txt + +echo -e "\n> $(tput setf 2)All Done! $(tput sgr0)Don't forget to configure plugins for your needs." +echo "> Run redstoner/server/start.sh to start the server" +echo "> Our plugins are in redstoner/server/plugins/redstoner-utils.py.dir" 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/snowbrawl.py b/snowbrawl.py index 1a6cea8..8cc371b 100644 --- a/snowbrawl.py +++ b/snowbrawl.py @@ -49,7 +49,7 @@ class Arena(object): self.match_goal = None # amount of deaths or time until the match ends (depends on arena_type) self.arena_type = None # arena type (death or time) self.explosion_damage = None - + self.player_stats = {} self.players = Queue() self.spawn_location = [] @@ -60,7 +60,7 @@ class Arena(object): self.corner1 = None # The corner1 given by the player (really bad for 3D position compare) self.corner2 = None # The corner2 given by the player (really bad for 3D position compare) self.tpp = None # The top, positive x, positive z corner - self.bnn = None # The bottom, negative x, negative z corner + self.bnn = None # The bottom, negative x, negative z corner #set corners of arena def set_corner(self, sender, type): @@ -75,21 +75,21 @@ class Arena(object): #Compares the corner1 and corner2 locations and figures out tpp and bnn so that we dont have to do it everytime we run in_arena() def update_corner_points(self): self.tpp = None - self.bnn = None + self.bnn = None if self.corner1 == None or self.corner2 == None: return corn1 = self.corner1.get_location() corn2 = self.corner2.get_location() if not corn1.getWorld().getName() == corn2.getWorld().getName(): return - + top = corn1.y if corn1.y > corn2.y else corn2.y bottom = corn1.y if corn1.y < corn2.y else corn2.y pos_x = corn1.x if corn1.x > corn2.x else corn2.x pos_z = corn1.z if corn1.z > corn2.z else corn2.z neg_x = corn1.x if corn1.x < corn2.x else corn2.x neg_z = corn1.z if corn1.z < corn2.z else corn2.z - + self.tpp = Coords(corn1.getWorld(), pos_x, top, pos_z, 0, 0) self.bnn = Coords(corn2.getWorld(), neg_x, bottom, neg_z, 0, 0) @@ -118,9 +118,9 @@ class Arena(object): self.start_match() return True return False - + #remove a player from the queue - def remove_player(self, player): + def remove_player(self, player): if self.queue.contains(player): self.queue.remove(player) return True @@ -132,7 +132,7 @@ class Arena(object): return False loc_tpp = self.tpp.get_location() loc_bnn = self.bnn.get_location() - + if loc.y > loc_tpp.y: return False if loc.y < loc_bnn.y: @@ -179,12 +179,12 @@ class Arena(object): alist[i] = alist[i+1] alist[i+1] = temp return alist - + @make_synchronized #Jython synchronized block def end_match(self): #End match, sort the players and print the 3 players with least amount of deaths - + sorted_list = self.bubbleSort(self.players.read()) - + for player in self.players.read(): if player.isOnline(): loc = self.sign_location[0].get_location().get_location() @@ -256,7 +256,7 @@ class Arena(object): def get_click_signs(self): return self.sign_click - + #Check if player is in the queue def in_queue(self,player): if self.queue.contains(player): @@ -307,7 +307,7 @@ class Arena(object): sign.get_location().set_location(location) break - #Remove location out of location + #Remove location out of location def delete_location(self, name, type): if type == "spawn": for spawn in self.spawn_location[:]: @@ -359,11 +359,11 @@ class Arena(object): return data def load(self, data): - self.explosion_damage = float(data["explosion"]) - self.player_limit = int(data["players"]) - self.refill = int(data["refill"]) + self.explosion_damage = None if str(data["explosion"]) == "None" else float(data["explosion"]) + self.player_limit = None if str(data["players"]) == "None" else int(data["players"]) + self.refill = None if str(data["refill"]) == "None" else int(data["refill"]) self.arena_type = str(data["type"]) - self.match_goal = int(data["goal"]) + self.match_goal = None if str(data["goal"]) == "None" else int(data["goal"]) self.corner1 = Coords(None).load(data["corners"][0]) if not data["corners"][0] == None else None self.corner2 = Coords(None).load(data["corners"][1]) if not data["corners"][1] == None else None self.tpp = Coords(None).load(data["corners"][2]) if not data["corners"][2] == None else None @@ -472,16 +472,16 @@ class Queue(object): def __init__(self): self.queue = [] - + #Appends to queue def put(self,args): self.queue.append(args) - + #Returns the first item in the queue and removes it def get(self): if len(self.queue) > 0: return self.queue.pop(0) - + else: return False @@ -498,7 +498,7 @@ class Queue(object): if player in self.queue: return True return False - + #Clear the queue def clear(self): self.queue = [] @@ -532,7 +532,7 @@ class timings_runnable(Runnable): self.arena = arena def run(self): - self.arena.end_match() + self.arena.end_match() #timings thread to end arenas if their type is time def timings(): @@ -545,10 +545,10 @@ def timings(): if arena.start_time + arena.match_goal < current_time: timing = timings_runnable(arena) server.getScheduler().runTask(server.getPluginManager().getPlugin("RedstonerUtils"), timing) - + time.sleep(0.1) - + timingsThread = threading.Thread(target = timings) timingsThread.daemon = True #Thread dies if main thread dies timingsThread.start() @@ -561,7 +561,7 @@ timingsThread.start() # Events ############################################################################################## -@hook.event("player.PlayerMoveEvent", "high") +@hook.event("player.PlayerMoveEvent", "low") def onMove(event): if event.getPlayer().getWorld().getName() != "minigames": return @@ -663,7 +663,7 @@ def on_quit(event): ############################################################################################## # Command handling -############################################################################################## +############################################################################################## def create_arena(sender, args): diff --git a/synchronizeranks.py b/synchronizeranks.py index 97439e0..4509185 100644 --- a/synchronizeranks.py +++ b/synchronizeranks.py @@ -3,6 +3,14 @@ from secrets import * import mysqlhack from com.ziclix.python.sql import zxJDBC +""" +WORK IN PROGRESS +""" + +#-----------------------Config-------------------------- + +config_file = "website-roles" + ranks = { "member" : 3, "builder" : 7, @@ -13,33 +21,42 @@ ranks = { "admin" : 5 } +ranks = open_json_file(config_file, ranks) + +def save_ranks(): + save_json_file(config_file, ranks) + +#-----------------------Event--------------------------- @hook.event("player.PlayerJoinEvent", "normal") def on_player_join(event): user = event.getPlayer() uuid = uid(player).replace("-", "") - role = get_role(uuid) - if role in [1, 2, 6]: #Disabled/Banned/Superadmin - return - if role: - for rank in ranks: - if user.hasPermission("group." + rank): - if role != ranks[rank]: - set_role(uuid, ranks[rank]) - return - if not user.hasPlayedBefore(): - return - if role == None: - msg(user, "&cYou haven't registed yet! Make sure to do so on redstoner.com") + sql_instruction + + def callback_thing(role, args): + + if role in [1, 2, 6]: #Disabled/Banned/Superadmin + return + if role != None: + for rank in ranks: + if user.hasPermission("group." + rank): + if role != ranks[rank]: + set_role(uuid, ranks[rank]) + elif user.hasPlayedBefore(): + msg(user, "&cYou haven't registed yet! Make sure to do so on redstoner.com") def get_role(uuid): - return execute_query("SELECT `role_id` FROM users WHERE `uuid` = ? LIMIT 1", uuid)[0][17] + results = execute_query("SELECT `role_id` FROM users WHERE `uuid` = ? LIMIT 1;", uuid) + return results[0][0] + # Returns a table with 1 row (LIMIT 1) and 1 column (SELECT `role_id`), so we're looking for the first row of the first column. def set_role(uuid, role_id): - execute_update("UPDATE users SET `role_id` = %d WHERE `uuid` = ?" % role_id, uuid) + execute_update("UPDATE users SET `role_id` = ? WHERE `uuid` = ?;", (role_id, uuid,)) + # %d is like %s for integers (unlogically, you'd expect something like %i), though %s also works here. def execute_query(query, uuid): @@ -56,5 +73,40 @@ def execute_update(update, uuid): conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") curs = conn.cursor() curs.execute(update, (uuid,)) + conn.commit() curs.close() - conn.close()
\ No newline at end of file + conn.close() + +def get_role(uuid): + sql_instruction() + +#--------------------------------Queries / Updates---------------------------- + +def sql_instruction(instruction, args, fetch = True, callback_func = ignored_func, callback_args = tuple()): + thread = threading.Thread(target = curs_instruction, args = (instruction_executor, instruction, fetch, callback_func, callback_args)) + thread.start() + + +def curs_instruction(func, instruction, fetch, callback_func, callback_args): + conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") + curs = conn.getCursor() + + if fetch: + returned = func(curs, instruction, fetch) + curs.close() + conn.close() + callback_func(returned, callback_args) + + else: + func(curs, instruction, fetch) + conn.commit() + curs.close() + conn.close() + + +def instruction_executor(curs, instruction, fetch): + curs.execute(instruction) + return curs.fetchall() if fetch else None + +def ignored_func(*args): + pass
\ No newline at end of file @@ -0,0 +1,78 @@ +from helpers import * + +add_perm = "utils.tag.add" +del_perm = "utils.tag.del" +check_perm = "utils.tag.check" + +data = open_json_file("tag", {}) + +@hook.command("tag") +def command(sender, command, label, args): + if len(args) > 0: + if str(args[0]) == "add": + if sender.hasPermission(add_perm): + if len(args) > 2: + add(sender, args[1:]) + else: + msg(sender, "&a-&c Usage: /tag add <name> <reason>") + else: + noperm(sender) + elif str(args[0]) == "check": + if sender.hasPermission(check_perm): + if len(args) == 2: + check(sender, args[1:]) + else: + msg(sender, "&a-&c Usage: /tag check <name>") + else: + noperm(sender) + elif str(args[0]) == "del": + if sender.hasPermission(del_perm): + if len(args) == 3: + delete(sender, args[1:]) + else: + msg(sender, "&a-&c Usage: /tag del <id>") + else: + msg(sender, "&a-&c Unknown subcommand! (add, check, del)") + else: + msg(sender, "&a&c Usage: /tag add/check/del") + return True + +def delete(sender, args): + player = server.getOfflinePlayer(args[0]) + uuid = uid(player) + try: + if data[uuid] == None: + pass + except: + msg(sender, "&a-&e There are no notes about this player") + return + if int(args[1]) - 1 >= len(data[uuid]): + msg(sender, "&a-&c Id of note is out of range") + return + del (data[uuid])[int(args[1]) - 1] + save_json_file("tag", data) + msg(sender, "&a-&e Deleted note at %s" % args[1]) + +def add(sender, args): + player = server.getOfflinePlayer(args[0]) + uuid = uid(player) + try: + if data[uuid] == None: + pass + except: + data[uuid] = [] + data[uuid].append(" ".join(args[1:])) + msg(sender, "&a-&e Note added") + save_json_file("tag", data) + +def check(sender, args): + player = server.getOfflinePlayer(args[0]) + uuid = uid(player) + try: + num = 0 + for tag in data[uuid]: + num += 1 + msg(sender, "&a-&e %s: %s" % (str(num), str(tag))) + except: + msg(sender, "&a-&e There are no notes about this player") + diff --git a/thread_utils.py b/thread_utils.py new file mode 100644 index 0000000..ec13c6f --- /dev/null +++ b/thread_utils.py @@ -0,0 +1,24 @@ +import threading +""" +Quick implementation of a @synchronized and @asynchronized decorators +""" + +#To be replaced by bukkit scheduler. +""" +def sync(lock=None): + def decorator(wrapped): + def wrapper(*args, **kwargs): + with lock: + return wrapped(*args, **kwargs) + return wrapper + return decorator +""" + +def async(daemon = True): + def decorator(function): + def wrapper(*args,**kwargs): + thread = threading.Thread(target=function,args=args,kwargs=kwargs) + thread.daemon = daemon + thread.start() + return wrapper + return decorator
\ No newline at end of file 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 "") diff --git a/vanishfix.py b/vanishfix.py new file mode 100644 index 0000000..b13b059 --- /dev/null +++ b/vanishfix.py @@ -0,0 +1,14 @@ +from helpers import * +from java.lang import Runnable + +class run(Runnable): + + def run(self): + players = server.getOnlinePlayers() + for player in players: + if player.hasPermission("essentials.vanish"): + player.performCommand("vanish") + player.performCommand("vanish") + +def enabled(): + server.getScheduler().runTaskTimer(server.getPluginManager().getPlugin("RedstonerUtils"), run(), 20, 1200) diff --git a/wrapper.py b/wrapper.py new file mode 100644 index 0000000..ddca5b5 --- /dev/null +++ b/wrapper.py @@ -0,0 +1,11 @@ +""" +Adapter classes for spigot api for more idiomatic python code. + +Before you run away from this if the class you need to use isn't here, please create it. + + +""" +from helpers import * +from wrapper_event import * +from wrapper_player import * +from wrapper_command import *
\ No newline at end of file diff --git a/wrapper_command.py b/wrapper_command.py new file mode 100644 index 0000000..dd51534 --- /dev/null +++ b/wrapper_command.py @@ -0,0 +1,11 @@ +from wrapper_player import * + +def command(command = "help"): + def decorator(wrapped): + @hook.command(command) + def wrapper(sender, command, label, args): + try: + return wrapped(sender = py_players[sender], command = command, label = label, args = args) + except: + print(print_traceback()) + return decorator
\ No newline at end of file diff --git a/wrapper_event.py b/wrapper_event.py new file mode 100644 index 0000000..083bf33 --- /dev/null +++ b/wrapper_event.py @@ -0,0 +1,22 @@ +from wrapper import * +from wrapper_player import * +from traceback import format_exc as print_traceback + +class py_event: + def __init__(self,event): + self.event = event + try: + self.player = py_players[event.getPlayer()] + except: + warn("Player doesn't exist") + +def event_handler(event_name = None, priority = "normal"): + def decorator(wrapped): + @hook.event(event_name, priority) + def wrapper(event): + try: + wrapped(py_event(event)) + except: + print(print_traceback()) + return decorator + diff --git a/wrapper_player.py b/wrapper_player.py new file mode 100644 index 0000000..aca0e60 --- /dev/null +++ b/wrapper_player.py @@ -0,0 +1,89 @@ +import time +import mysqlhack +from mysql_utils import * +from thread_utils import * +from players_secret import * +from datetime import datetime +from com.ziclix.python.sql import zxJDBC + +class py_player: + def __init__(self,player): + self.player = player + self.login_time = time.time() + self.logging_in = False + + self.nickname = self.name + self.registered = False + self.password = "None" + self.banned = False + self.banned_reason = "You have been banned!" + self.played_time = time.time() - self.login_time + self.last_login = datetime.now() + self.first_seen = datetime.now() + + def kick(self, kick_message = "You have been kicked from the server!"): + self.player.KickPlayer(kick_message) + + @property + def name(self): + return self.player.getName() + + @property + def uuid(self): + return str(self.player.getUniqueId()) + + +class Py_players: + def __init__(self): + self.players = [] + + def __len__(self): + return len(self.players) + + def __getitem__(self, player): + for py_player in self.players: + if py_player.name == player.getName(): + return py_player + else: + return None + + def remove(self, player): + self.players.remove(player) + + def append(self, player): + self.players.append(player) + +py_players = Py_players() + +@async(daemon=True) +def fetch_player(player): + with mysql_connect() as sql: + sql.execute("SELECT * FROM utils_players WHERE uuid = ?", (player.uuid,)) + result = sql.fetchall() + + if len(result) is 0: + with mysql_connect() as sql: + sql.execute("INSERT INTO utils_players \ + (uuid, name, nickname, registered, password, banned, \ + banned_reason, played_time, last_login, first_seen) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + + args=(player.uuid, player.name, player.nickname, player.registered, + player.password, player.banned, + player.banned_reason, player.played_time, + player.last_login, player.first_seen)) + else: + pass + + + +@hook.event("player.PlayerJoinEvent","lowest") +def on_join(event): + player = py_player(event.getPlayer()) + py_players.append(player) + fetch_player(player) + + +@hook.event("player.PlayerQuitEvent","highest") +def on_leave(event): + py_players.remove(py_players[event.getPlayer()]) |