Python Plugin Loader
PythonLoader 0.3.4
This plugin allows developers to code bukkit plugins in the python programming language. The plugins in python can either use a thin wrapper to the standard Bukkit API, or the newer decorator API. For more detailed instructions for installing this plugin and how to code plugins, take a look into the Readme on Github.
Features:
- Load bukkit plugins written in python.
Installation: Put PythonLoader.jar into plugins directory and jython.jar into lib directory (within bukkit folder not plugins folder).
Downloads:
- PythonLoader 0.3.4
- Jython (By downloading and using Jython you accept the Jython License)
- Source Code
Documentation
The Readme on github contains an introduction to creating plugins in python
Also make sure to check out @zaph34r's tutorial series
I have to give special credits to lahwran who created the whole decorator API and corrected many of my mistakes. :)
and
both work fine for me, although personally i would use the first version for clarity. Is there anything else around that could throw the exception? Wrong bukkit or jython version? some other incompatibility? The IndexOutOfBoundsException at least hints strongly at something being wrong with bukkit's internals, not with your code itself.
What version of bukkit/jython/python-loader do you use?
Hey I love that plugin. I tried to create a world, but If I try that I always get a error. This is the line that I used, no problems by a java plugin:
world = server.createWorld(bukkit.WorldCreator.name("testworld"))
Error:
18:01:49 [SCHWERWIEGEND] org.bukkit.plugin.InvalidPluginException: Traceback (mo st recent call last): File "<iostream>", line 13, in <module> at java.util.ArrayList.RangeCheck(Unknown Source) at java.util.ArrayList.get(Unknown Source) at org.bukkit.craftbukkit.CraftServer.getDefaultGameMode(CraftServer.jav a:1052) at org.bukkit.craftbukkit.CraftServer.createWorld(CraftServer.java:666) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source)
java.lang.IndexOutOfBoundsException: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
18:01:49 [SCHWERWIEGEND] at net.lahwran.bukkit.jython.PythonPluginLoader. loadPlugin(PythonPluginLoader.java:296) 18:01:49 [SCHWERWIEGEND] at net.lahwran.bukkit.jython.PythonPluginLoader. loadPlugin(PythonPluginLoader.java:113) 18:01:49 [SCHWERWIEGEND] at net.lahwran.bukkit.jython.PythonPluginLoader. loadPlugin(PythonPluginLoader.java:83) 18:01:49 [SCHWERWIEGEND] at org.bukkit.plugin.SimplePluginManager.loadPlu gin(SimplePluginManager.java:305) 18:01:49 [SCHWERWIEGEND] at com.master.bukkit.python.PythonLoader.onLoad( PythonLoader.java:58) 18:01:49 [SCHWERWIEGEND] at org.bukkit.craftbukkit.CraftServer.loadPlugin s(CraftServer.java:218) 18:01:49 [SCHWERWIEGEND] at org.bukkit.craftbukkit.CraftServer.<init>(Cra ftServer.java:189) 18:01:49 [SCHWERWIEGEND] at net.minecraft.server.ServerConfigurationManag er.<init>(ServerConfigurationManager.java:53) 18:01:49 [SCHWERWIEGEND] at net.minecraft.server.MinecraftServer.init(Min ecraftServer.java:157) 18:01:49 [SCHWERWIEGEND] at net.minecraft.server.MinecraftServer.run(Mine craftServer.java:423) 18:01:49 [SCHWERWIEGEND] at net.minecraft.server.ThreadServerApplication. run(SourceFile:492)
yes,
server
should also be available, containing the current server instance@zaph34r
Alright , and the same is true for server ?
hey, thanks for pulling my changes :)
@Malcolm2608
That should be correct, you can access
bukkit
as a global variable. So you can usebukkit.Material.STONE
orbukkit.ChatColor.White
from anywhere.in your changelog for the latest version of your plugin, you put this: org.bukkit is now automatically imported as bukkit does that bukkit is a global variable that is automatically imported ?
Hey guys, a new version 0.3.3 is out, yay!
At first I really want to thank Zaph34r! He's the one who wrote the new annotations which are actually the biggest part of this update and also quite complex.
If you want to know what exactly has changed you should check out the changelog, for the rest i'll just sum up the most important parts here:
1) The @EventHandler and @CommandHandler Annotations
These two new decorators work exactly like the old @hook.event and @hook.command methods, but they have the big advantage that they also work on bound methods. In order to use them you first have to create a class which extends Listener or PythonPlugin. Then you add the decorators to your event handlers and finally you have to create an instance of the listener class. It is important to note that you have to create at least one instance during the loading of the plugin, otherwise the handlers are registered as static methods.
What's also interesting about the new @EventHandler is that you can use it without parameters as long as the method name equals the name of the event.
So here is an example how to use the new annotations:
2) log class
zaph34r created a small piece of code which I found in almost every plugin I saw and he also used it in the Annotation code so I decided to include it directly in the loader.
So you now have 3 methods available for logging purposes:
You can also set a prefix to all log messages via log.prefix
Here's an example how to use them:
@Cwbh10
Just create a .zip archive containing the .py and the .yml at root level (using 7zip or winzip or winrar, or whatever else),and rename it from .zip to .pyp or .py.zip :)
@zaph34r
Thanks a lot Zaph34r! That really help clear things up now, and I should be able to generate more accurate results xD!
So, will there be a place where we can post out plugins for others to see?
P.S. How do I create a zip the contains the yml and py that pyplugin loader will see, I've tried but it doesn't seem to see it. (Sorry for all the questions :P )
@Cwbh10 Ok so heres how the
global
keyword works (according to my knowledge, which might be wrong :D ):print a
ora.foo()
), you need to do nothing special, apart from not having a local variable of the same name shadowing it.a=0
in global scope, and want to change that variable witha = 1
, you need to explicitely declare the variable as a global variable withglobal a
, because otherwise python would create a local variablea
and assign the value1
to it (which is the default). This local variable would shadow the globala
until you leave the scope (the local variablea
would cease to exist after that, and the global one would be unchanged) So basically,global a
tells python "do not create a new local variable a when i assign to that name later on in the current scope". So if you don't plan to assign anything new to the variable, you don't needglobal
, that is why it works in your event handler if you just checkif immediate:
You should also generally do all
global
declarations at the beginning of the scope in which you want to use them, and not somewhere in the middle, and not use any local variables of the same name before usingglobal
, otherwise you get a syntax warning from the interpreter.There is also not a lot of stuff pre-imported, off the top of my hat, you can access:
Everything else you have to import, which is why the first few lines of any plugin i do are
since i use those all the time :)
There may be more pre-imported things in the future, i just today changed how the modification i did works, and that would allow for pre-loading certain boilerplate code automatically without lots of effort. I will look into that :)
@zaph34r
Oh, So I have to immediately define it as a global before each use? But wouldn't that break my function in the Event handler wouldn't it? Though it did work once I added the global in the command handler.
I've been able to get it to declare outside the onEnable but I still have to call it as a global in the command handler but not the event handler.... (Confused face)
I'm just slightly confused how globals are being handled throughout the program. No you're not bragging I just think your pretty boss xD
Thnxs for the help btw
P.S. Is there a list of pre-imported globals that Python does for me; I saw I had to import org.bukkit to access the colors which I thought was already done for me...
@Cwbh10
I certainly do hope i didn't come across as boasting :D The modified loader (that should be pulled into the main repository soon i think) does add a few things for convenience. There's a writeup of what it does here. The rationale behind the changes/additions was to make decorators work with instance methods, and the use of additional decorators was to change as little about the original python loader as possible, to avoid incompatibilities.
Regarding your problem with the globals:
This works for me, and is probably how you need to do it. I think
global immediate
is used to access a global variable, not to define it. You just useimmediate = something
, the keywordglobal
is used to distinguish between accessing the global variable in the local scope and creation of a new local variable (and possibly shadowing a global one). You only need it when assigning to the global variable, since assignment would create a new local variable otherwise. When just accessing it (as i do here withprint immediate
beforeglobal immediate
) it just looks for the name scope by scope until it either finds it,or doesn't find anything even in global scope, in which case it would throw a NameError.Regarding chat colors: use the bukkit.ChatColor enumeration, but remember that you cannot concatenate it with strings (since the ChatColor type doesn't have concatenation with strings defined), you need to format it in, like
@masteroftime I deleted the old code from the other plugin, but just ran into the same issue again. I've tried the global declaration in and out of the onEnable... It just limits what I can do ya know? Anyways, please try to attack me about my code's neatness xD
I get the error: " File "<iostream>", line 74, in immediates UnboundLocalError: local variable 'immediate' referenced before assignment"
But I don't see what it says it's local... I've also tried calling the global inside the same function, but in the previous function the global worked in a few functions before ceasing to work in later ones..
P.S. How do I get color in the chat for my plugin? I've looked around (e.g. http://jd.bukkit.org/doxygen/df/d1d/ChatColorTest_8java_source.html) but I still can't find a way to get it into my plugins...
@Cwbh10
Could you maybe post code which produces the error here or as a ticket?
Okay, while I've been loving the PythonPlugin Loader, it hasn't been without it's faults.
I'm having a rather major issue in that the fact that I want to have global variables across my script, and for some reason after I declare the variable outside any function and set the variable in the onEnable() it isn't recognized in later functions. I also have tried putting the "global var" inside the onEnable() and it only seems to recognize the global for another 3 functions out of the 3+. I've spent many hours on this so if anyone could help that would be great! Besides that, PythonPlugin Loader as been a great tool!
Ps. @zaph34r what is this modified pythonplugin loader you boast about in your tutorial? What are the changes in using it. I've notice you write differently than I do in your examples... any reason for the choice?
I have started a collection of Tutorials for Python Plugin Development, that might help some people getting started. They contain practical example plugins and a step-by-step walkthrough on how to create them and what each part does.
Feel free to direct any requests for additional tutorial topics or specific things that you would like to have explained in more detail to me and i will see what i can do :)
THANKS.
<3 Python.
why should bukkitdev not work? I host my plugins (both python and java) on bukkitdev, and i can't see why it would be a problem
@zaph34r
THANK YOU SOOOOOO MUCH.
Oh, and last question, is there a place where we can share the python plugins we do ? I somehow doubt using bukkitdev will work :P
@roblabla
checked real quick with ingame interpreter and got:
so i guess that works, economy and chat should work the same
to translate the example from vault directly with that:
would turn into