If I recall correctly, the particular concrete class that's being selected is determined at runtime via reflection. Because Minecraft is not open source, all the developers have are the obfuscated compiled class files to work with.
The code searches through each class file within the minecraft jar, searching for a class that matches certain conditions, and then, using a bytecode library, force that class to implement that interface.
For example, let's say that the following (obfuscated) class was the real Server
class within the Minecraft code
class a {
String x_x317() {
return q_q98;
}
static a a_a1;
static String q_q98 = "1.9.4";
}
In this case, the method x_x317
returns the version string. The tool that allows them too hook into this class might do it based on the following conditions:
- The class has default access
- The class has only one default access static reference to itself
- The class has only one default access static String field.
- The class has a single method, that has default access, that returns
String
, and the returned value is the FieldRef
found in 3.
This generally returns only one class. In the case that multiple are returned (usually in the dev phase of the new Bukkit version), they get more specific with their conditions to ensure that they only get the right class returned. They do this for every field, class, and method they need to identify.
Since they now know which exact class is the Server
class, they can go ahead and make changes to it. First they would need to implement the interface
class a implements org.bukkit.Server
And then implement the method
class a implements org.bukkit.Server {
String x_x317() {
return q_q98;
}
public String getVersionNumber() {
return x_x317();
}
static a a_a1;
static String q_q98 = "1.9.4";
}
Now, we have a class that conforms to the Bukkit API.
When they need to instantiate that class, they just do something along the lines of
Server server = findAndTransformServerClassFromMinecraftJar();
// ...
Server findAndTransformServerClassFromMinecraftJar() {
// load classes from jar
// map them to the appropriate interfaces
// transform and hook the required classes and methods
Class<?> serverClass = doTheFirstThreeSteps();
return (Server) serverClass.newInstance();
}