-
Notifications
You must be signed in to change notification settings - Fork 7
Home
Linkstone aims to support plugins that depend on internal implementation classes of CraftBukkit. These classes are in the package net.minecraft.server
and org.bukkit.craftbukkit
, often referred as NMS
and OBC
.
To achieve this goal, linkstone provides code that has the exact same names as NMS and OBC but calls Glowstone implementations instead.
Writing those classes is simplified by multiple annotations. The most important ones are @LClassfile
, @LMethod
, @LConstructor
and @LField
. Classes, methods, constructors and fields that exist in NMS/OBC code must be annotated with those. If you use one of those annotations, you'll have to define the CraftBukkit version that you targeted while writing the code.
We'll use the @LMethod
annotation as a showcase. The principals below do also apply to @LClassfile
, @LConstructor
and @LField
.
import static net.glowstone.linkstone.annotations.Version.*;
import net.glowstone.linkstone.annotations.*;
// This method, with this behavior exists in CraftBukkit 1.11 R1 and 1.12 R1
@LMethod(version = { V1_11_R1, V1_12_R1 })
public int getViewDistance() {
return glowPlayer.getViewDistance();
}
// This example method exists only in CraftBukkit 1.12 R1.
// If we compile linkstone for version 1.11, this method will not be present.
@LMethod(version = V1_12_R1)
public boolean isSpectator() {
return glowPlayer.isSpectator();
}
A lot of methods and fields in CraftBukkit have not been deobfuscated. They have random names like a
or b
.
Instead of assigning these strange names to our handwritten methods we can use the annotations.
They allow us to obfuscated methods:
// If we compile linkstone for version 1.11 this method method will be named "a".
// When compiling linkstone for version 1.12 it's named "b".
@LMethod(version = V1_11_R1, name = "a")
@LMethod(version = V1_12_R1, name = "b")
public int getViewDistance() {
return glowPlayer.getViewDistance();
}
The behavior of a method might change between multiple CraftBukkit versions.
Let's say we got a method that calculates the distance between two entitys. In CraftBukkit 1.11 it might have returned the distance while it returns the squared distance in CraftBukkit 1.12.
We could express this as follows:
// This method exists only for CraftBukkit 1.11.
// It returns the distance.
@LMethod(version = V_11_R1)
public int getDistance(Entity that) {
int xdiff = this.x - that.x;
int ydiff = this.y - that.y;
int zdiff = this.z - that.z;
return Math.sqrt(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff);
}
// This method exists for Craftbukkit 1.12 and returns the squared distance.
// If we do also call it "getDistance" we will get a compilation error so we had to rename it.
// To fix that, we assign the correct name in the annotation.
@LMethod(version = V_12_R1, name = "getDistance")
public int getDistance_v12(Entity that) {
int xdiff = this.x - that.x;
int ydiff = this.y - that.y;
int zdiff = this.z - that.z;
return xdiff * xdiff + ydiff * ydiff + zdiff * zdiff;
}
What if we override an annotated method? Therefore we got the @LOverride
method.
The compiler ensures that the overridden method has a @LMethod
annotation and applies it to the overriding method.
Here's an example where the @LMethod
annotation will also be applied to the overriding method.
Both methods are present in version 1.12 and will be renamed to "z".
public class EntityHuman {
@LMethod(version = V1_12_R1, name = "z")
public void isCreative() {
return false;
}
}
public class EntityPlayer extends EntityHuman {
private GlowEntity glow;
@LOverride
public boolean isCreative() {
return glow.isCreative();
}
}