Skip to content
Aki edited this page Jul 10, 2018 · 8 revisions

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 and @LField. Classes, Methods 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 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();
}

What about obfuscation?

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();
}

Multiple versions, multiple behaviors?

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;
}
Clone this wiki locally