Welcome to the final round with the old but reliable Java language. There surprisingly was little difference to setting up the Pokémon TRPS bot on Java compared to the Dovahzul Bot. Gradle specifies the dependencies and Pokémon TRPS required nearly all the same ones as Dovahzul. In fact, less setup time was required due to my prior experience with Dovahzul and fewer dependencies. Look below for the list of dependencies.
implementation("net.dv8tion:JDA:4.3.0_277")
compile 'io.github.cdimascio:java-dotenv:5.2.2'
compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
Blogger decided it would be a good idea to break on the night I am working on this blog. The upload image button seems to be broken, so I will mostly resort to shorter copy-able code snippets. I assuming that my audience read the previous Dovahzul Java post, therefore I will be skipping over some technical details that should not need to be fully restated. If you did not read the Dovahzul Java post yet or you are unfamiliar with JDA (Java discord API), I highly recommend you read it before the rest of this post.
Command Comparison to Python & TypeScript
Loading commands in Java cannot be done dynamically like Python cogs without significant overhead. Since Java is a statically typed compiled language, creating additional commands on the fly is a bit more tricky. I decided to create a command loader singleton class which stores command implementations into a command HashMap. The HashMap uses the name of the command as a key, therefore processing commands becomes way more streamlined within my code. The message listener which I named CommandListener, takes a sent message and tests whether it is a valid command to process. Once the command is fully parsed, the Driver's processCommand method executes.
Driver.getDriver()
.processCommand(commandName,jda,msg,msg.getAuthor(),messageArgs)
Each command implementation implements a Command interface, similar to the TypeScript version. They each are required to have an execute method which is then called by the command processor. The Java implementation for this felt like an incredibly verbose version of the TypeScript bot. I think there were benefits as my code was a lot easier and cleaner to read, however it required a significant amount of overhead.
|
|
|
lines 9-21, PlayCPUCommand.java |
Reactions in Java
Reactions in Java work quite differently than both Python and TypeScript. It definitely had a steeper learning curve for me, but once I learned what to do I liked it a lot. I implemented the reactions by first creating a reaction listener which calls the onMessageReactionAdd event from JDA. This event triggers every single time any reaction is added to a message. Since this triggers so often, especially on multi-server bots, a lot of filtering is needed. After receiving the user and emoji, I immediately test that the Java bot does is not triggering itself when it adds reaction. The removeReaction method then removes the added reaction so other users cannot see it.
return;
}
event.getReaction().removeReaction(user).queue();
Also interestingly enough, this version of the Bot is where the unique Game instance ID became quite crucial.
event.retrieveMessage().submit().whenComplete((message, throwable) -> {MessageEmbed embed = message.getEmbeds().get(0);if(embed==null){return;}String gameID = embed.getTitle();Game game = Driver.getDriver().getGameByID(gameID, message.getId());if(game==null || game.getStatus() != GameStatusType.COLLECT){return;}if(game.hasUser(user)){game.userSelect(user,emoji);}});
A completable future (returned by submit), which works similarly to a JavaScript promise, executes the provided callback after the retrieveMessage method succeeds or fails. Since the retrieval of the message is asynchronous, the completable future ensures that the method fully finishes before executing the callback. Within the callback we retrieve the Game ID, where we then retrieve the Game instance by comparing all active Game IDs to the Game ID embedded within the message. Using the found instance of the game, I call userSelect which processes the selection of the user.
Reaction Comparison to Python & TypeScript
In comparison to Python and TypeScript, I found completable futures to work very similar to TypeScript promises. Also since they can be easily stored into maps, arrays, or even merged into a single object, code design can be dramatically improved. I think the control-flow for the Java version is confusing compared to Python and TypeScript. Completable futures alleviate some of the confusion when implemented correctly. Also, I had to use the reaction listener since there was no Reaction Collector like in TypeScript.
Results
|
|
| player vs cpu |
|
|
| player vs player |
|
|
| flying type |
Final Remarks
I found the Java version of Pokémon TRPS to be by far the most painful to develop. Please keep in mind I consider myself way more proficient with Java than TypeScript and Python. Once I had the "busy-work" done, adding features and new commands became easier as time went on. The Java version also performed superiorly to TypeScript, matching Python in reaction request speed. After coding this bot, I believe python would still be the best choice for Pokemon TRPS. If working on a larger bot, I would definitely consider using Java. There are many other situations that Java may be potentially better suited. But for developing Dovahzul and Pokémon TRPS, Java was not worth the time.
Comments
Post a Comment