diff --git a/.gitignore b/.gitignore index f2c0185..ecd1139 100755 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,5 @@ *sublime* # these are player/ingame specific -files/chatgroups.json -files/lol.json -files/reports.json \ No newline at end of file +files/ +files/* \ No newline at end of file diff --git a/README.md b/README.md index 99cef7f..67c5167 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,19 @@ Redstoner's custom plugins, written in python. # Installation / Set-up +1-line-install-script for bash: +`wget -O install.sh "http://pastie.org/pastes/9310905/download?key=6byp4mrqmiui8yqeo3s6yw"; md5sum --check <<<"3499671c0832e561bc9c7d476d2167cb install.sh" && sh install.sh` + +Detailed description: + 0. Create a new directory called 'redstoner' -0. Download [the latest bukkit](http://dl.bukkit.org/downloads/craftbukkit/get/latest-dev/craftbukkit.jar) +0. Download [the latest spigot](http://ci.md-5.net/job/Spigot/lastStableBuild/artifact/Spigot-Server/target/spigot.jar) 0. Run it once inside redstoner, then shut it down again 0. Create a new directory (inside redstoner) called 'lib' 0. Download [jython](http://search.maven.org/remotecontent?filepath=org/python/jython-standalone/2.5.3/jython-standalone-2.5.3.jar) and save it as 'jython.jar' inside lib 0. Download [mysql-connector](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.30.zip), extract 'mysql-connector-java-X.X.XX-bin.jar ' and save it as 'mysql-connector.jar' inside lib -0. Download [PyPluginLoader](http://gserv.me/static/PyPluginLoader-0.3.5.jar) into the plugins directory +0. Download [PyPluginLoader](http://gserv.me/static/PyPluginLoader-0.3.5.jar) (we're using [this fork](https://github.com/gdude2002/Python-Plugin-Loader)) into the plugins directory 0. inside plugins, clone this directory into 'redstoner-utils**.py.dir**': `git clone git@bitbucket.org:redstonesheep/redstoner-utils.git redstoner-utils.py.dir` 0. Download [PEX](http://dev.bukkit.org/media/files/789/291/PermissionsEx.jar) into plugins @@ -25,18 +30,32 @@ Redstoner's custom plugins, written in python. Always use this branch to change code, please test before pushing. (If something goes wrong here, that's okay) * **master** - Never commit into this branch directly! Only merge stable versions of *dev* + Never commit into this branch directly! Only merge stable versions of *dev*: + ```bash + git checkout master + git merge dev + ``` -# Deploying +# Deploying on the server -There ain't much to do. The dev server uses the *dev* branch, the live server uses *master*. Just pull and restart the server. +**Never** edit the files directly on the server! +The dev server uses the *dev* branch, the live server uses *master*. -Be **very careful** with the live server! Make sure you're in the master branch and the code is working before pulling/restarting! +Please use the script `/git_pull_utils.sh`. +**Do not use `git pull` on the server!** +All files must be owned and pulled by the *redstoner* user. + +Be **very careful** with the live server! Make sure you're in the *master* branch and the code is working before pulling/restarting! # Modules / Files +If you add a new file, please update this list! + +If you want the server to load a file (*module*) on startup, add it to the `modules` list in `main.py`. + + * `files/` > All config / storage files go here @@ -88,4 +107,67 @@ Be **very careful** with the live server! Make sure you're in the master branch * `webtoken.py` - > Adds `/token`, reads and writes from the database to generate *pronouncable* (and thus memorable) registration-tokens for the website. \ No newline at end of file + > Adds `/token`, reads and writes from the database to generate *pronouncable* (and thus memorable) registration-tokens for the website + +* `spawnplayer.py` + + > Code that was used once to create [this](http://www.reddit.com/r/Minecraft/comments/28le52/screenshot_of_all_players_that_joined_my_server/) awesome [screenshot](http://i.imgur.com/v4wg5kl.png) + +* `tilehelper.py` + + > A plugin that automatically tiles (stacks) blocks inside a selected region in configurable directions. + +* `mentio.py` + + > Adds `/listen`, highlights chat and plays a sound when your name was mentioned + + +# Code styleguide & tips + +## Indentation +Never use tabs! +Use 2 spaces to indent. + +## Comments +Comments are good! +Please comment everything that's non-obious or makes it easier to understand + +## Debugging +Debugging can be a bit special with jython/PyPluginLoader. + +When something goes wrong, you probably see a weird traceback that's not telling you shit. +...unless you take a closer look. + +You will not see a direct traceback of the python methods. +Instead, you'll probably see a bunch of java, bukkit and python things, because it tries to translate python into java, line per line. +Take a closer look at the method names, they might tell you what it's trying to do and where it's coming from. + +Watch out for something like `org.python.pycode._pyx5.horribleCode$23(/path/to/badcode.py:214) ~[?:?]` +0. In this case, `_pyx5` is our module. +0. `horribleCode` is the method that was called +0. `/path/to/badcode.py` is the actual file of our module +0. `:214` is the line in which the error occured. + +Please note that the line may not be accurate. You'll often get the start or end of a loop, method, or the like - when the actual error was somewhere in there. + +In many cases, this is enough to find your bug. If you still cannot find it,try to catch exceptions in your code, as follows: + + +## Catching exceptions + +If you want to catch all exceptions (e.g. for debugging), do not: +```python +try: + # code +except Exception, e: + print(e) +``` +Since we're using jython, this will not catch Java exceptions. +This will give you some more deatails: +```python +import traceback +try: + #code +except: # everything + print(traceback.format_exc()) +``` \ No newline at end of file diff --git a/adminchat.py b/adminchat.py index 13a4003..5c07c50 100644 --- a/adminchat.py +++ b/adminchat.py @@ -42,7 +42,7 @@ def onAcCommand(sender, args): return True -@hook.event("player.PlayerChatEvent", "normal") +@hook.event("player.AsyncPlayerChatEvent", "low") def onChat(event): sender = event.getPlayer() msg = event.getMessage() diff --git a/chatgroups.py b/chatgroups.py index e354afb..4178ab2 100644 --- a/chatgroups.py +++ b/chatgroups.py @@ -12,6 +12,7 @@ try: except Exception, e: error("Failed to load chatgroups: %s" % e) + @hook.command("chatgroup") def onChatgroupCommand(sender, args): try: @@ -49,6 +50,7 @@ def onChatgroupCommand(sender, args): except Exception, e: error(e) + @hook.command("cgt") def onCgtCommand(sender, args): p = sender.getName() @@ -76,6 +78,7 @@ def groupchat(sender, message, ann=False): #except Exception, e: # error(e) + def saveGroups(): try: chatgroups_file = open(chatgroups_filename, "w") @@ -84,7 +87,8 @@ def saveGroups(): except Exception, e: error("Failed to write reports: " + str(e)) -@hook.event("player.PlayerChatEvent", "normal") + +@hook.event("player.AsyncPlayerChatEvent", "normal") def onChat(event): sender = event.getPlayer() msge = event.getMessage() diff --git a/main.py b/main.py index 4f1ddd3..30845ea 100644 --- a/main.py +++ b/main.py @@ -11,8 +11,9 @@ sys.path += ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/l try: from helpers import * -except Exception, e: - print("[RedstonerUtils] ERROR: Failed to import helpers: %s" % e) +except: + print("[RedstonerUtils] ERROR: Failed to import helpers:") + print(print_traceback()) @@ -36,8 +37,8 @@ for module in modules: try: mod[module] = __import__(module) log("Module %s loaded." % module) - except Exception, e: - error("Failed to import module %s: '%s'" % (module, e)) + except: + error("Failed to import module %s:" % module) error(print_traceback()) diff --git a/mentio.py b/mentio.py index d5d6460..02e49a3 100644 --- a/mentio.py +++ b/mentio.py @@ -7,7 +7,7 @@ arrow = colorify(u"&r&7\u2192&r") regex = reg_compile(u"\u00A7[\\da-fk-or]") -@hook.event("player.AsyncPlayerChatEvent", "normal") +@hook.event("player.AsyncPlayerChatEvent", "high") def onChat(event): try: if not event.isCancelled(): @@ -15,7 +15,7 @@ def onChat(event): words = event.getMessage().split(" ") recipients = event.getRecipients() - for recipient in recipients: + for recipient in list(recipients): rec_words = words[:] # copy for i in range(len(rec_words)): word = rec_words[i] @@ -25,13 +25,14 @@ def onChat(event): # player was mentioned if rec_words != words: - try: # list might not be mutable + try: recipients.remove(recipient) # don't send original message except: + # list might not be mutable, ignoring. Receiver will get the message twice pass message = " ".join([sender.getDisplayName(), arrow] + rec_words) msg(recipient, message, usecolor = False) recipient.playSound(recipient.getLocation(), "mob.chicken.plop", 1, 0) - except Exception, e: - error("Failed to handle PlayerChatEvent: %s" % e) + except: + error("Failed to handle PlayerChatEvent:") error(print_traceback()) \ No newline at end of file diff --git a/misc.py b/misc.py index 6d853e8..2945fbe 100644 --- a/misc.py +++ b/misc.py @@ -1,5 +1,6 @@ +#pylint: disable=F0401 from helpers import * - +import org.bukkit.inventory.ItemStack as ItemStack # # Welcome new players @@ -75,12 +76,20 @@ def onSudoCommand(sender, args): return True +# +# Clicking redstone_sheep with shears will drop redstone + wool and makes a moo sound +# - - - - - +@hook.event("player.PlayerInteractEntityEvent") +def onPlayerInteractEntity(event): + if not event.isCancelled(): + sender = event.getPlayer() + entity = event.getRightClicked() + if isPlayer(entity) and str(entity.getUniqueId()) == "ae795aa8-6327-408e-92ab-25c8a59f3ba1" and str(sender.getItemInHand().getType()) == "SHEARS" and str(sender.getGameMode() == "CREATIVE"): + for i in range(5): + entity.getWorld().dropItemNaturally(entity.getLocation(), ItemStack(bukkit.Material.getMaterial("REDSTONE"))) + entity.getWorld().dropItemNaturally(entity.getLocation(), ItemStack(bukkit.Material.getMaterial("WOOL"))) + sender.playSound(entity.getLocation(), "mob.cow.say", 1, 1) # diff --git a/webtoken.py b/webtoken.py index 4a15b2e..438e2ba 100644 --- a/webtoken.py +++ b/webtoken.py @@ -56,6 +56,8 @@ def token_command(sender): if token: msg(sender, "&aEmail: &e%s" % token[1]) msg(sender, "&aToken: &e%s" % token[0]) + msg(sender, "&cIMPORTANT: never share the token with anyone!") + msg(sender, "&cIt could be used to claim your website account!") else: msg(sender, "&cYou don't have a token yet! Use &e/tokengen &c.") except Exception, e: @@ -94,7 +96,7 @@ def onTokenCommand(sender, args): thread.start_new_thread(token_command, (sender,)) return True -@hook.command("tokengen") +@hook.command("gettoken") def onTokengenCommand(sender, args): thread.start_new_thread(tokengen_command, (sender, args)) return True