In this informal tutorial, I write about how you can use Java’s dynamic proxies to handle Bukkit’s ProxiedCommandSender
in a more natural way.
So I was planning to have a paragraph at the start here where I would ramble on about what Minecraft is and what Bukkit/Spigot are, but if you’re reading this post (based on the title), then I don’t think I need to do that in too much detail. Simply put, this blog post is talking about Bukkit, a Minecraft server software, but this post also applies to Spigot (a fork of Bukkit). I’m assuming you have a decent understanding of how the Bukkit API works, because today I’m talking about Bukkit’s CommandSender
s.
The problem with the ProxiedCommandSender
class
So, Bukkit has this interface called the ProxiedCommandSender
. It’s a fairly simple interface with two methods getCaller()
and getCallee()
:
1
2
3
4
5
package org.bukkit.command;
public interface ProxiedCommandSender extends CommandSender {
CommandSender getCaller();
CommandSender getCallee();
}
The getCaller()
method returns a CommandSender
which refers to the command sender that “sent” the command to the callee. The getCallee()
method returns a CommandSender
, referring to the command sender that will actually end up executing the command. To illustrate this, say we run the following Minecraft command from the console:
1
/execute as @e[type=chicken,limit=1] run say Hello!
In this scenario, our getCaller()
method will return the console, in particular an instance of Bukkit’s ConsoleCommandSender
class. Similarly, our getCallee()
method will return a chicken from the current world.
So, what’s wrong with this you ask? Sure, it sounds fairly intuitive, but in practice, it’s just another annoying case to handle. Picture the scenario, you’re writing a simple command and want to check who can and cannot run said command. The only way to do this is to use the instanceof
keyword in Java, which leads to the very bloaty pseudocode:
1
2
3
4
5
6
7
8
9
public boolean onCommand(CommandSender sender ... /* omitted */) {
if (sender instanceof Player) {
// Code
}
else if (sender instanceof Entity) {
// Code
}
else if (sender instanceof ...)
}
But since the ProxiedCommandSender
has a second sender, to properly account for the possibilities you have to do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean onCommand(CommandSender sender ... /* omitted */) {
if (sender instanceof Player) {
// Code
} else if (sender instanceof Entity) {
// Code
}
// And so on
} else if (sender instanceof ProxiedCommandSender) {
CommandSender callee = ((ProxiedCommandSender) sender).getCallee();
if (callee instanceof Player) {
// Code
} else if (callee instanceof Entity) {
// Code
}
// And so on
}
}
If your code wasn’t bloaty already, it most certainly is bloaty now! So you’re probably wondering “hey, what if we just make that callee
object the sender and have a check for that at the beginning?”. Unfortunately that leads to another, more annoying problem: permissions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean onCommand(CommandSender sender ... /* omitted */) {
if (sender instanceof ProxiedCommandSender) {
sender = ((ProxiedCommandSender) sender).getCallee();
}
if (sender instanceof Player) {
if (sender.isOp()) {
// Do something
}
}
else if (sender instanceof Entity) {
// Code
}
// And so on
}
If we ran the command from a caller with elevated permissions, we basically want to temporarily “apply” those permissions to the callee. Using the example above, if we ran a command from the console, we would expect the target callee that will be running the command to actually be able to run the command, instead of the callee being told that they don’t have permissions to do anything.
This is better explained by the implementation of the ProxiedCommandSender
class. Let’s have a little look at this in more detail before we explain how to combat this issue.
The ProxiedNativeCommandSender class
The ProxiedCommandSender
class is merely an interface. It’s underlying implementation is actually the ProxiedNativeCommandSender
class, which basically implements CommandSender
with the following rules:
- If you send a message to the
ProxiedCommandSender
, the message goes to the caller. - If you want to check the permissions of the
ProxiedCommandSender
, or want to see if the sender is op, it will check the permissions or op status of the caller. - If you get the name of the
ProxiedCommandSender
, it returns the name of the callee.
As you can see, the ProxiedNativeCommandSender
implementation redirects the permission checks to the caller, instead of the callee.
So, to summarize what our problem is:
We want a
CommandSender
that is basically thegetCallee()
from the proxied command senderWe want a
CommandSender
which has the same permissions as thegetCaller()
methodWe don’t want to hassle around with referring to the
ProxiedCommandSender
’s two different methods all the time, we want to handle this in one go, with something along the lines of:1 2 3 4 5 6 7 8 9 10
public boolean onCommand(CommandSender sender ... /* omitted */) { sender = mergeProxySender(sender); if (sender instanceof Player) { // Code } else if (sender instanceof Entity) { // Code } // And so on }
Introducing Dynamic Proxies
Java has this very niche feature called dynamic proxies. Dynamic proxies are a way of creating instances of an instance of an interface (or multiple interfaces) using reflection. Before we get into the nitty-gritty, let’s start by having a look at what it consists of:
- An
InvocationHandler
, which is basically our implementation of the class that we want to construct. This is basically where the main body of the code will go. - The
Proxy.newProxyInstance
method, which creates an instance of ourInvocationHandler
, and applies whatever interfaces we want.
The InvocationHandler
The InvocationHandler
is the brains behind everything. In the context of our problem that we’re trying to solve, we’ll use this to create a CommandSender
class that is basically the same as ProxiedCommandSender.getCallee()
, except if you ever try to access any permission-related things, we’ll redirect the method call to ProxiedCommandSender.getCaller()
.
The InvocationHandler
interface has one method, invoke
. This method takes in 3 parameters:
- Itself
- The method that it’s going to call
- The arguments to apply to the method that it’s going to call
If you’re familar with reflection, then this will be a little simpler to understand. This method invoke
is basically a “man-in-the-middle”. Let’s say we have the following code in our onCommand()
method:
1
2
3
4
5
public boolean onCommand(CommandSender sender ... /* omitted */){
if (sender instanceof Player) {
Bukkit.broadcastMessage(sender.isOp());
}
}
Normally, if you ran this, then the following would occur:
\[\texttt{sender.isOp()}\rightarrow\texttt{CraftPlayer.isOp()}\rightarrow\texttt{boolean result}\]
Except since we have an InvocationHandler
, that intercepts the invocation of a method, we have something more like this:
\[\texttt{sender.isOp()}\rightarrow\texttt{InvocationHandler}\rightarrow\texttt{boolean result}\]
So, let’s take a look at some example code, shall we? The following class ProxiedSenderHandler
implements InvocationHandler
. We then implement the invoke
method and then our main logic begins:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ProxiedSenderHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
switch(method.getName()) {
case "isPermissionSet":
case "hasPermission":
case "addAttachment":
case "removeAttachment":
case "recalculatePermissions":
case "getEffectivePermissions":
case "isOp":
case "setOp":
//Run the getCaller()'s method
default:
// Run the getCallee()'s method
}
}
}
Here, we’ve got a very simple switch case to determine which methods should be run using the caller as the command sender and which methods should be run using the callee as the command sender. Since we want all of the permissions checks to be run using the caller, we simply denote that in our switch statement.
First, we need to get an instance of ProxiedCommandSender
to finish off this method. To do this, we’ll add a really simple constructor that takes in the ProxiedCommandSender
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ProxiedSenderHandler implements InvocationHandler {
private ProxiedCommandSender proxySender;
public ProxiedSenderHandler(ProxiedCommandSender proxySender) {
this.proxySender = proxySender;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
switch(method.getName()) {
case "isPermissionSet":
case "hasPermission":
case "addAttachment":
case "removeAttachment":
case "recalculatePermissions":
case "getEffectivePermissions":
case "isOp":
case "setOp":
return method.invoke(proxySender.getCaller(), args);
default:
return method.invoke(proxySender.getCallee(), args);
}
}
}
Alright, now we’ve got all of that sorted, we can now create an instance of our CommandSender
!
Using Proxy.newProxyInstance
The Proxy.newProxyInstance
method is fairly scary, but when you get used to it, it’s pretty simple.
1
static Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
This method requires 3 parameters: the classloader, which is used to define this “proxy class”, an array of interfaces that this proxy class should inherit and our invocation handler.
For the ClassLoader
, we’ll simply use the class loader of our primary interface, in this case CommandSender
. This parameter isn’t too important and can basically be any class loader because the majority of classes are all loaded from the same class loader.
Next, we want to declare our interfaces. This is basically a list of interfaces that our “proxy class” will inherit from. If we think about it for a bit, we want our class to be a CommandSender
, but it should also inherit everything else from the callee. If our callee were a Player
, then we would expect this class to also implement Player
. As such, we can use the getInterfaces()
method from our callee’s class.
Lastly, we simply create an instance of our ProxiedSenderHandler
class that we’ve defined earlier and we’re done:
1
2
3
4
5
6
7
ProxiedCommandSender proxy = ...;
Proxy.newProxyInstance(
CommandSender.class.getClassLoader(),
proxy.getCallee().getClass().getInterfaces(),
new ProxiedSenderHandler(proxy)
);
Putting everything together
Our last step is to piece everything together. In the code example that I stated at the beginning of this post, I came up with a method called mergeProxySender
that takes in a CommandSender
and will return our custom CommandSender
that merges the information of the ProxiedCommandSender
into it:
1
2
3
4
5
6
7
8
9
10
11
12
13
CommandSender mergeProxySender(CommandSender sender) {
if (sender instanceof ProxiedCommandSender) {
ProxiedCommandSender proxy = (ProxiedCommandSender) sender;
return (CommandSender) Proxy.newProxyInstance(
CommandSender.class.getClassLoader(),
proxy.getCallee().getClass().getInterfaces(),
new ProxiedSenderHandler(proxy)
);
} else {
return sender;
}
}
And it’s as simple as that! Now, we don’t have to perform all of the extra checks for whether the command sender is a ProxiedCommandSender
or not - we can simply write our code as if the CommandSender were simply a Player
or an Entity
or whatever.
Conclusions
To conclude, we managed to transform the ProxiedCommandSender
object into a simple CommandSender
object which is basically the ProxiedCommandSender.getCallee()
, except if you try to check its permissions, it’ll return the permissions of ProxiedCommandSender.getCaller()
. We did this to reduce the amount of extra type checks and casting that is needed and it allows our commands to be run via a proxy, such as an entity or another command sender.
To do all of that, we created an InvocationHandler
and then used Proxy.newProxyInstance()
to create a Proxy
class that inherits from the callee.
That’s pretty much it!
Appendix A: InvocationHandler lambdas
Since the InvocationHandler
interface only has one method, we can use a lambda to save us the effort of creating a whole class with a constructor for the ProxiedCommandSender
!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CommandSender mergeProxySender(CommandSender sender) {
if (sender instanceof ProxiedCommandSender) {
ProxiedCommandSender proxySender = (ProxiedCommandSender) sender;
return (CommandSender) Proxy.newProxyInstance(
CommandSender.class.getClassLoader(),
proxy.getCallee().getClass().getInterfaces(),
(Object proxy, Method method, Object[] args) -> {
switch(method.getName()) {
case "isPermissionSet":
case "hasPermission":
case "addAttachment":
case "removeAttachment":
case "recalculatePermissions":
case "getEffectivePermissions":
case "isOp":
case "setOp":
return method.invoke(proxySender.getCaller(), args);
default:
return method.invoke(proxySender.getCallee(), args);
}
}
);
} else {
return sender;
}
}
Appendix B: Removing that switch statement
Don’t like switch statements because it makes the code too tall? Just use Array.asList()
and use the contains()
method to filter the method name:
1
2
3
4
5
6
7
8
9
10
(Object proxy, Method method, Object[] args) -> {
if (Arrays.asList("isPermissionSet", "hasPermission", "addAttachment",
"removeAttachment", "recalculatePermissions", "getEffectivePermissions",
"isOp", "setOp").contains(method.getName())
) {
return method.invoke(proxySender.getCaller(), args);
} else {
return method.invoke(proxySender.getCallee(), args);
}
}
Appendix C: I want super condensed code!
Is the code still too much for you, even after using a lambda and removing a switch statement? Use the ol’ ternary operator:
1
2
3
4
5
6
7
(Object proxy, Method method, Object[] args) -> {
return method.invoke(
Arrays.asList("isPermissionSet", "hasPermission", "addAttachment",
"removeAttachment", "recalculatePermissions", "getEffectivePermissions",
"isOp", "setOp").contains(method.getName()) ? proxySender.getCaller() : proxySender.getCaller(),
args);
}