Home Simplifying Bukkit ProxiedCommandSenders
Post
Cancel

Simplifying Bukkit ProxiedCommandSenders

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 CommandSenders.

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 the getCallee() from the proxied command sender

  • We want a CommandSender which has the same permissions as the getCaller() method

  • We 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 our InvocationHandler, 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);
}
This post is licensed under CC BY 4.0 by the author.

Adding a page table of contents to mdBook

Hotswappable Spigot Plugins