I have a couple plugins of mine to port but I wasn’t sure if I should start now or wait until an official release. I guess I could start taking a look at the API docs to see what I would require, but still the question remains; should I start now or wait?
You can look at the API docs, but the entire thing is subject to change at the time being so I would not start porting yet or you may have a lot of changes to make in the near future as development proceeds.
The API is not even done yet, and even if it was ready it could still change. Wait.
I gues you better wait. Things can change rapidly.
Wait it out. It’s not going to be very hard to port plugins, assuming that you do not use much NMS code.
You can look through the API as it is now, but it would most likely change from now, to the time that it’s released, so it maybe best to wait until the official build of it is released before trying to port anything over.
Alright yeah that’s what I thought. Thanks all!
In their eagerness to help you, people tend to forget to really think about their answers before posting. I think what people are trying to say is that if the goal of your project is to only interact directly with a large subset of the Sponge API, and nothing else, then you do not really have much of a choice. Since the current state of the API is a jumbled mess of useless pull requests, bad ideas, and a couple of sane people trying to keep everything together with duct tape and hot glue, you don’t really have anything to program up against, and trying to do so will only net your a big Bukkit of frustration when things are changed around.
The thing is, this whole situation of one API submerging into the sea of unsupportability, and another one emerging from the sea of opportunities, is actually a free lesson in software architecture that a great too many people either pass up on, or simply don’t understand (mostly the latter). A lot of people are throwing around terms like “abstraction layer”, “API” and “interfaces”, without really understanding what it means, and to what degree they can be applied. All they know (or think they know) is that they need the Sponge API to make plugins. There is some truth to this, but this question is a little more vague than “can I make Sponge plugins yet?” - I interpret it as the slightly more general, and much more interesting question of “can I start to work on what will eventually become a Sponge plugin yet?”, and the answer is a resounding “YES!”.
For an explanation, a wall-of-text is necessary. If you are afraid of perspective, scroll past this reply or click the Back-button in your browser… Okay, ready?
How to order a pizza
Abstraction layers generally consist of two parts. The first and (arguably) most important part is descriptions of how something works, guarantees, if you will. Or better yet, specifications. The point of a specification is to give you, the programmer, something to work from right off the bat. This approach of designing software is commonly referred to as programming by contract. A good specification will be very precise, and tell you exactly what kind of guarantees components that satisfy it make. The specification of the service provided by a pizzeria could be something along the lines of:
“Given a number on the menu (a natural number between 1 and 25) and the amount of money next to the menu item with the given number, you will be given a pizza that contains the toppings mentioned in the description of said menu item.”
This may sound like an over-complicated way of saying “pick a number, pay, get pizza”, but notice how the extra information makes it very clear exactly what kind of input the service expects, and what kind of output you can expect to get back. Given this specification and the associated menu, and assuming that all pizzerias have exactly the same 25 pizzas (which, let’s be honest, they do), you are basically able to order pizza from any pizzeria, without even knowing about any of them. The specification is decoupled from the actual service(s), but because the specification is so elaborate, you can still ask everyone at your party which pizzas they want and plan to order them, once you find a pizzeria that’s open and satisfies the specification.
In Java, an interface represents a specification. You cannot instantiate an interface, because its methods have no method bodies. But a Java interface explains how a class that implements it should behave. Thus, a class that correctly implements an interface according to its specification, the JavaDocs, satisfies the specification and thus guarantees to work according to it. When you call a method like
getPlayer("garbagemule"), what you get is an object of an interface type. You know that there is an underlying implementation (because otherwise the object couldn’t exist), and assuming the implementation is correct, you can call the Player methods and expect them to function correctly.
"This is not a pipe"
The painting The Treachery of Images by René Magritte is an ever-important painting in the world of computer science, because it can help people understand the difference between a thing and a representation of said thing. As an example, if you make a String in Java that contains the two characters
3, you have a String, not a number. Even though
"23" clearly reads the number twenty-three, it is indeed a String of two symbols that represent the numbers 2 and 3. In the same way, the famous painting by Magritte is not a pipe - it is a painting of a pipe.
Now draw the parallel to the Player object. When you call methods on that object, you are programming to the Player interface, not the underlying implementation. You are actually just given a representation of a player, but you are still “using it” as if it was a player (note that even the underlying implementation of the interface is still technically just a representation of a player). You have absolutely no idea if the object returned even implements the methods you call correctly, but you assume that they do. And that’s all you need to write your plugin.
Abstracting away the abstractions
“So what?” you say. “I’m still stuck, because the Player interface isn’t even ready yet!” This is when you take the induction step: Abstraction layers can be defined recursively as being either a layer on top of the underlying implementation, or a layer on top of another layer. Thus, you know that the abstraction layer can be trusted at a given level, so adding another layer that you control can give you enough expressive power to work on the core of your plugin. With a sufficiently “thin” abstraction layer, you will basically be able to write a Sponge plugin in which only maybe 10% of the actual code involves the Sponge API (when it is ready).
Confusing? No worries, I’ll walk the walk and give you something concrete…
Putting it all together
Say I want to make a simple plugin that allows me to type the command
/items <item list>, where
<item list> is a space-separated list of items that I want to give to the player that executes the command. An item is specified in the format
<name> is the internal item name, and
<amount> is a
number representation of a number that denotes the amount of the resulting item stack.
To implement such a plugin, I will need to somehow tell Sponge to run a specific method, providing a
player representation of the player who typed the command, as well as the command arguments. However, nothing keeps me from creating a class that takes on the responsibility of turning the command arguments into a more useful representation of the items to give. Assuming all Minecraft items will have String identifiers prepended with
"minecraft:", I could turn all the substrings on the form
<name>:<amount> into an object containing nothing but a String on the form
"minecraft:<name>", and an
int resulting from calling
Notice how shifting the responsibility of splitting arguments, adding a prefix, and parsing a number into a different class than the “command handler” (which I cannot implement yet) allows me to actually work on something now already. The amount of code required in the “command handler” is reduced, or more generally, the amount of code in a class that will depend on the Sponge API has been reduced. This is huge, because I can also very easily write unit tests for such a class and verify that it does what it should, even before the Sponge API is ready.
Now scale things up. Take something like a minigame plugin. You will most likely have to associate some information with each player, so why not define your own representation of a player? Create a custom Player interface that defines methods you will need, such as
teleport(Position). Notice how I didn’t use
Location (likely candidates)? You can also create your own representations of these entities.
Eventually, you will have to write Sponge-specific code. At that point, and when the API is ready, you can create a class that implements your representation. How? By having your implementation hold a reference to the Sponge-representation of the player, and simply redirecting your custom method calls to the relevant methods on it.
When things get cofusing, take a step back, close your eyes, and think about Magritte’s painting. Then, pretend your friend runs a pizzeria, and practice making the actual order from him (wax on, wax off). When the time finally comes to order a real pizza, you are prepared, and you can focus on understanding what the pizza guy is saying (which can sometimes be difficult) instead of worrying about whether or not the 23 on the menu is a natural number or just a representation, or if pizza no. 23 has pineapple on it.
You hit the nail square on the head and the pedagogical presentation of the design methodology is wonderful. I hope it takes root with more developers.
Our server mostly relies on in-house plugins except for a few notable exceptions - mcMMO, LogBlocks, GriefPrevention, etc. We started following a similar approach several months ago of separating core logic from Bukkit’s implementation, more so for smoothing the transition from Bukkit to the forever promised, continually delayed Modding API; alas, its no surprise that we still have to wait. Fortunately, our efforts were not in vain, since it has helped us tremendously in positioning ourselves to rapidly adapt to Sponge.
For now, we are implementing Forge versions of our Bukkit plugins by abstracting the abstractions. As @garbagemule suggested, we use logical representations of what we have dubbed Minecraft Primitives - player, location, world, actions/“events”, blocks, items, etc. These primitives maintain attributes and characteristics that will not change - unless Mojang changes or adds them - and ensures consistency regardless of what modding API is implemented.
This might be a bit more daunting task if our adapter layer had to support every conceivable Bukkit/CraftBukkit artifact. For now it is fairly lightweight since we only implement what we need. When it comes time to switch to Sponge or whatever else may fall from the sky, its a matter creating an adapter and testing with very little refactoring or redesign.
However, I would caution that this is not a panacea either. A few of our management plugins work with CraftBukkit innards and are tightly coupled with its design and implementation; even so, we can still abstract all the “window dressing” making it somewhat less of an arduous conversion task.
You sir, deserve a cookie.
I think its better to wait. Its still in development and things can make a left turn. Wait!