code relationship between scriptcraft and spigot/bukkit/canary/minecraft

54 views
Skip to first unread message

Landru 27

unread,
Dec 28, 2017, 5:41:36 PM12/28/17
to ScriptCraft - Scripting Minecraft
Hello -

ScriptCraft is awesome, and is just the right thing for my main goal : teaching my children and friends' children programming in a way that they find engaging.

I deeply appreciate all of the time, effort, and personal investment that has gone into creating ScriptCraft, and keeping it up-to-date with recent developments, such as MC v1.12, Spigot, etc.

I have successfully set up a handful of Minecraft/Spigot/ScriptCraft servers, and everything is working quite well thus far.

While the kids work through the Young Person's Guide webpage, and soon the Beginner's Guide book (shipping from England -- Peach Pit and Amazon US were both out-of-stock!), I'm pursuing some more involved mods.

Approaching it all with a 25-year career in software engineering, a great deal of it makes sense to me, but I do have a question that I hope one of you can answer for me:

I see how to use the ScriptCraft API, and I see that I can use the Spigot API, and I have done both with success.  I also see that the ScriptCraft API itself uses the Spigot API, but I'm slightly lost on exactly how this is being accomplished.

For example, I see in lib/scriptcraft.js the lines:

    Canary = Packages.net.canarymod.Canary;    and
    Bukkit = Packages.org.bukkit.Bukkit;

but I cannot find where 'Packages' is defined, and *how* it accomplishes access to the Canary/Bukkit/Spigot Java class methods and such.  Is 'Packages' something that is defined by dint of using Rhino / Nashorn (which I gather provides the interconnection between Java and JavaScript) ?

Also, I see things like:

    Packages.net.minecraft.block.BlockDoor.EnumHingePosition

for which I have the same question, and a related question:  I see that there is a correspondence between that hierarchy of identifiers and the hierarchy of Java sourcecode, but the correspondence is only approximate in some places;  for example, the sources from which the server code is built has:

    .../CraftBukkit/src/main/java/net/minecraft/server/BlockDoor.java
    .../Spigot/CraftBukkit/src/main/java/net/minecraft/server/BlockDoor.java
    ...Spigot/Spigot-Server/src/main/java/net/minecraft/server/BlockDoor.java

Those are close to the "net.minecraft.block.BlockDoor" hierarchy, but not exact.  Also, which of these is being referenced?  or some other code path altogether?  (the line in question is from the 'drone' module 'doors.js' file).

I feel like I'm close to understanding the code relationship here, but I also feel like I'm missing something.  Can anyone shed some light here for me?

Once I have a firmer grasp of the relationship between ScriptCraft's workings and Minecraft/Bukkit/Spigot's workings, I'd like to contribute to this excellent project, mainly by expanding the ScriptCraft API.  For example, it's great that ScriptCraft's inventory.js has methods for adding, removing, and querying for items in the player's inventory.  I'd like to add to that, such as a function to enumerate the inventory, inspect specific inventory slots, etc.

Any information anyone can provide on the above two code-relationship questions would be quite welcome.

And thank you again to Walter and everyone else working on ScriptCraft, for such an awesome platform!

- Andrew

Landru 27

unread,
Dec 29, 2017, 12:59:09 AM12/29/17
to ScriptCraft - Scripting Minecraft
Growing more confident that 'Packages' must be supplied by the Java-to-Javascript interface software, I felt confident enough to frame a question that Google would grok, and I've confirmed that, indeed, it is exactly what I guessed:  a global object that Nashorn makes available, which "enables you to access Java packages and classes using their fully qualified names, as if they are properties of the Packages object."  (quoted from the Nashorn Java API at docs.oracle.com).

So that answers that question, and leaves my other question, which could be perhaps rephrased like this:

If I wanted to add shortcuts to my ScriptCraft plugins and modules similar to the existing "Bukkit = Packages.org.bukkit.Bukkit;", how do I know which of the several code paths among the build sources that have ".../src/main/java/org/bukkit/..." in them that I'd be referring to?  similarly, how do I make sense of the uses of "Packages.net.minecraft....", in this same context, in case using something similar to that seems best for a given task at hand?

Insights, pointers, hints and/or explanations are welcome.  Thanks!

- Andrew

Walter Higgins

unread,
Dec 30, 2017, 5:28:00 AM12/30/17
to ScriptCraft - Scripting Minecraft
The Bukkit API (Java) is documented here https://hub.spigotmc.org/javadocs/bukkit/

I recommend always using org.bukkit instead of net.minecraft (usually referred to as NMC) classes.

Walter

Landru 27

unread,
Dec 30, 2017, 12:54:58 PM12/30/17
to ScriptCraft - Scripting Minecraft
Hi Walter -

Thanks for your reply, but that's not my question.  The location of the Bukkit API is well documented in the material you have built.  I've used it quite a bit, with good results.

Please allow me to clarify my question.  I'll do it with a real-world example that I am working on.

I'm writing a plugin that allows the player to cast a specific enchantment at a specific level.  (I dislike the random nature of Minecraft's normal enchantment mechanic.)  I'm also making it so that the player must expend reagents to cast the enchantment, and so that the player can expend blocks of redstone in place of experience levels.

I actually have all of this working quite well, but there were points where the Bukkit API docs were not entirely illuminating.

Specifically, ...

The API doc for org.bukkit.inventory.ItemStack has:
addEnchantment(Enchantment ench, int level)
So now I need to have an Enchantment object ...

The API doc for org.bukkit.enchantments.Enchantment has [1]:
getByName(String name)
So now I need to know the allowable input values for 'name'.

The question I'm asking could be phrased for this example as:

"What can I read to find out the allowable input values for org.bukkit.enchantments.Enchantment.getByName() ?"

Again, this is an example.  I'm really asking the general question, "What can I read to find out the *details* for using the Bukkit API?"

I framed my initial question in terms of the code relationship because I know that -- by definition -- reading the source code will tell me authoritatively about the details beyond what is revealed by the API docs.

As it turns out, I found the answer for the valid 'name' values for org.bukkit.enchantments.Enchantment.getByName(name), but through some experimentation.  After I had the answer, I used it to search 'backwards' through the Java source files that are downloaded/created during the standard Spigot installation process, but it would have been more direct to work my way forward.  But for that, I would need to know the best/proper starting point.  Hence, my question.


Here's a second example:

In your arrows.js plugin, you call events.projectileHit( __plugin.bukkit ? onBukkitArrowHit : onCanaryArrowHit).

Focusing on just the Bukkit part of this, this calls events.on(org.bukkit.event.entity.ProjectileHitEvent, callback, priority).

This registers onBukkitArrowHit() as a callback for ProjectileHitEvent events.

The Bukkit API docs for org.bukkit.event.entity.ProjectileHitEvent show that as a subclass of org.bukkit.event.entity.EntityEvent it has an 'entity' field, which shows how you are able to (in onBukkitArrowHit()) assign "projectile = event.entity".  So far, so good.

But then you assign "shooter = projectile.shooter", and I don't see where the Bukkit API docs make it clear that this is possible.  'Entity' does not have a field 'shooter', nor does 'Arrow'.

But, I see in the Java source files in .../net/minecraft/server/EntityArrow.java that EntityArrow has a public field 'shooter'.  But in the directory tree created by the standard Spigot installation, I have four EntityArrow.java files:

./CraftBukkit/src/main/java/net/minecraft/server/EntityArrow.java
./Spigot/CraftBukkit/src/main/java/net/minecraft/server/EntityArrow.java
./Spigot/Spigot-Server/src/main/java/net/minecraft/server/EntityArrow.java
./work/decompile-cf6b1333/net/minecraft/server/EntityArrow.java

Hence, again, my question about how best to read the source code in looking for details *beyond* the Bukkit API docs.

Much more succinctly, when *you* wrote the arrows.js plugin, how did *you* know that there was a 'shooter' field you could make use of?

I see that the Projectile class has a getShooter() method, so is there a relationship between getShooter() and the filed .shooter?  Is there something that turns all of the Bukkit classes' getters into fields?  If so, what about getters that aren't named getNNNN?  (like Projectile.doesBounce()) ?  And what naming convention is used?  More generally :  Where can I go to find out this kind of detail that bridges the Bukkit API to ScriptCraft?

I've many ideas for contributing to ScriptCraft, and some insight into this lower-level stuff would help me contribute in that way.

Thanks again for everything that ScriptCraft is, and all that you've put into it.

- Andrew


[1]  org.bukkit.enchantments.Enchantment also has:
Enchantment(int id), but I need an existing Enchantment, not a new instantiation; and
getById(int id), but that's deprecated, and I'd need to know the valid values for 'id', just as with getByName()

guillau...@magicmakers.fr

unread,
Dec 30, 2017, 1:32:36 PM12/30/17
to ScriptCraft - Scripting Minecraft
Le 30/12/2017 à 18:54, Landru 27 a écrit :
Hi Walter -

Thanks for your reply, but that's not my question.  The location of the Bukkit API is well documented in the material you have built.  I've used it quite a bit, with good results.

Please allow me to clarify my question.  I'll do it with a real-world example that I am working on.

I'm writing a plugin that allows the player to cast a specific enchantment at a specific level.  (I dislike the random nature of Minecraft's normal enchantment mechanic.)  I'm also making it so that the player must expend reagents to cast the enchantment, and so that the player can expend blocks of redstone in place of experience levels.

I actually have all of this working quite well, but there were points where the Bukkit API docs were not entirely illuminating.

Specifically, ...

The API doc for org.bukkit.inventory.ItemStack has:
addEnchantment(Enchantment ench, int level)
So now I need to have an Enchantment object ...

The API doc for org.bukkit.enchantments.Enchantment has [1]:
getByName(String name)
So now I need to know the allowable input values for 'name'.

The question I'm asking could be phrased for this example as:

"What can I read to find out the allowable input values for org.bukkit.enchantments.Enchantment.getByName() ?"

Again, this is an example.  I'm really asking the general question, "What can I read to find out the *details* for using the Bukkit API?"

I framed my initial question in terms of the code relationship because I know that -- by definition -- reading the source code will tell me authoritatively about the details beyond what is revealed by the API docs.

As it turns out, I found the answer for the valid 'name' values for org.bukkit.enchantments.Enchantment.getByName(name), but through some experimentation.  After I had the answer, I used it to search 'backwards' through the Java source files that are downloaded/created during the standard Spigot installation process, but it would have been more direct to work my way forward.  But for that, I would need to know the best/proper starting point.  Hence, my question.


For all enum - like class like Enchantment (and there is a ton in minecraft), you can simply check spigot doc (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/enchantments/Enchantment.html) - you will get the full list of fields, and you can use getByName() with the field name (which is always in caps lock for easier reading, e.g ARROW_DAMAGE, ARROW_FIRE ...). You can also use values() on the class itself (it's a static method) to get the whole array of available enchantment (again, this work with all enum - like class like enchantment.


Here's a second example:

In your arrows.js plugin, you call events.projectileHit( __plugin.bukkit ? onBukkitArrowHit : onCanaryArrowHit).

Focusing on just the Bukkit part of this, this calls events.on(org.bukkit.event.entity.ProjectileHitEvent, callback, priority).

This registers onBukkitArrowHit() as a callback for ProjectileHitEvent events.

The Bukkit API docs for org.bukkit.event.entity.ProjectileHitEvent show that as a subclass of org.bukkit.event.entity.EntityEvent it has an 'entity' field, which shows how you are able to (in onBukkitArrowHit()) assign "projectile = event.entity".  So far, so good.

But then you assign "shooter = projectile.shooter", and I don't see where the Bukkit API docs make it clear that this is possible.  'Entity' does not have a field 'shooter', nor does 'Arrow'.

But, I see in the Java source files in .../net/minecraft/server/EntityArrow.java that EntityArrow has a public field 'shooter'.  But in the directory tree created by the standard Spigot installation, I have four EntityArrow.java files:

./CraftBukkit/src/main/java/net/minecraft/server/EntityArrow.java
./Spigot/CraftBukkit/src/main/java/net/minecraft/server/EntityArrow.java
./Spigot/Spigot-Server/src/main/java/net/minecraft/server/EntityArrow.java
./work/decompile-cf6b1333/net/minecraft/server/EntityArrow.java

Hence, again, my question about how best to read the source code in looking for details *beyond* the Bukkit API docs.

Much more succinctly, when *you* wrote the arrows.js plugin, how did *you* know that there was a 'shooter' field you could make use of?

I see that the Projectile class has a getShooter() method, so is there a relationship between getShooter() and the filed .shooter?  Is there something that turns all of the Bukkit classes' getters into fields?  If so, what about getters that aren't named getNNNN?  (like Projectile.doesBounce()) ?  And what naming convention is used?  More generally :  Where can I go to find out this kind of detail that bridges the Bukkit API to ScriptCraft?

If a java class has a getX() setX() methods, they should be automatically bound to object.X (taking in account camelCase, so getShooter() gives object.shooter and not object.Shooter), thanks to nashorn. I don't believe this kind of detail is documented somewhere tho :(.
This work in the general case, for stuff like doesBounce / setBounce, as far as i know it isn't mapped to object.bounce ; you have to use the methods.

If you are unsure about stuff like that, the easiest way is to simply try ;-) My favorite method is exporting to global the object I want to look at (if it's in an event, otherwise i just return it), and then using Tab/Autocomplete to see what kind of fields I have on it


I've many ideas for contributing to ScriptCraft, and some insight into this lower-level stuff would help me contribute in that way.

Thanks again for everything that ScriptCraft is, and all that you've put into it.

- Andrew


[1]  org.bukkit.enchantments.Enchantment also has:
Enchantment(int id), but I need an existing Enchantment, not a new instantiation; and
getById(int id), but that's deprecated, and I'd need to know the valid values for 'id', just as with getByName()


P.S : shame you can't answer directly by mail to the groups, by the way :(

Landru 27

unread,
Dec 30, 2017, 2:41:38 PM12/30/17
to ScriptCraft - Scripting Minecraft
Thanks, Guillaume, for that info.  That helps quite a bit.

After posting, I went browsing through the github repo of ScriptCraft itself, and found this:


which has the same explanation about getters and setters.  That and what you said confirm my guess, and that certainly helps with '.shooter', as well as the use of "projectile.location", also used in arrows.js;  it's now clear that this is due to the available getLocation() method.

And, it's revealing that these are just shortcuts, reaffirming that the methods themselves are also available, for things like doesBounce().  When programming, even when I use magical things like this, I like to know where the magic begins and ends, because ultimately, as a programmer, I'm not content with "it's magic!"  :-)

For the Enchantment names, I found exactly what you are saying :  it turned out that the allowable 'name' values match the fields on the same class.  I was surprised by this, because I initially expected I'd need to use names like 'Respiration', but the in-code name for this is 'OXYGEN'.

To find those legal 'name' values, I used a method similar to what you suggested, dumping the output of Enchantments.values() to the console log.  But I also assumed that I got lucky there : it might be that not all classes have such a values() method, it might be that the output of values does not include the inputs that I'm looking for, and (at the time) I felt like I could not rely on the class having a set of fields like that.  It's a good coding practice, but of course the language does not enforce that kind of code organization, so I was anticipating the need to paw through source code at times.  I'm glad to hear that it's consistent enough to follow the pattern you mention.

I also agree with the "just try" approach.  It worked for my item enchantment plugin, for understanding Enchantments, Materials, the PlayerInventory, and a couple of other spots.  But, it can be time-consuming in some circumstances, so I was reaching out to see if there were a more direct approach.  The info you've provided will save me considerable time, knowing that I can rely on certain patterns.

Thanks again for your reply; it's been quite helpful indeed!

- Andrew

Reply all
Reply to author
Forward
0 new messages