-
Notifications
You must be signed in to change notification settings - Fork 7
Declaration annotations
Writing linkstone classes is supported by multiple annotations. They are attached to every (package), class, field, enum constant, constructor and method that is also present in NMS/OBC classes. They are called @LPackage
, @LClassfile
, @LField
, @LEnum
, @LConstructor
and @LMethod
respectivly. 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 for the declaration annotations. The principals shown below do also apply to @LClassfile
, @LConstructor
, @LEnum
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
annotation.
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 extends EntityLiving {
@LMethod(version = V1_12_R1, name = "z")
public void isCreative() {
return false;
}
}
public class EntityPlayer extends EntityHuman {
private GlowPlayer glow;
@LOverride
public boolean isCreative() {
return glow.isCreative();
}
}