Write Version Dependent Code
While our API is entirely version-independent for you to use, you might still need to implement some version-dependent things yourself (such as niche things, just something we didn't implement). At the current moment, you might need to implement more yourself, as we're still implementing more and more stuff for you to use version independently.
There are two ways to implement version-dependent; different use cases require different ways to implement them.
The easiest method is to create an interface or abstract class and implement it in your desired version(s). This will result in the ability that you can control your version-dependent code from the core.
The more complex way would be SpongePowered's Mixin. While Mixin is comparatively easy to use, it can be a bit confusing at the beginning, as all new things are.
Important Note
Please keep in mind that the examples on this page are version dependent. We are showing the examples for Minecraft 1.19.1, so depending on the date you're reading this, recreating the examples might not be possible, we'll try to keep them updated, though, and if we do, we update the version in this note.
Access the Minecraft Code "the Normal Way"
You should only use this method for things you can access without using Reflections, as using Reflections can have a high impact on the performance of the players that use your addon.
The use case we will implement in the following example will show how to display messages in the player's chat. We have already implemented this, but it is relatively easy to show and understand.
We will first create a new interface in our core
- or api
module called ExampleChatExecutor
, annotate it with the @Referenceable
annotation and declare a new
method void displayMessageInChat(String)
. Now we head to the module of the version we want and create a new class
called VersionedExampleChatExecutor
in our desired package. If there is no folder called src
in the module, you'll
need to create the following folder structure inside the module: src/main/java/
.
Now to the implementation. First, we implement the interface ExampleChatExecutor
, and now the most important part: we
need to add the annotation Implement
to the class and declare ExampleChatExecutor.class
as the argument. This will
allow you to access the versioned implementation from the core module. Then we'll add the annotation Singleton
to the
class, as we don't need more than one object of this implementation. At this point, the only missing point is the actual
implementation of Minecraft; we'll overwrite the displayMessageInChat
method from our interface and create a new
Component from our String with Component.literal(message)
. Then we access the Chat GUI
with Minecraft.getInstance().gui.getChat()
and add our component with addMessage(component)
.
To access our VersionedExampleChatExecutor
from the core module, we need to add a private instance of the ExampleChatExecutor
with a getter to our ExampleAddon
class. Then we assign the instance in our ExampleAddon#enable
method using ((DefaultReferenceStorage) this.referenceStorageAccessor).exampleChatExecutor()
. If the function does not exist, we need to run a gradle build to add the class to our reference storage. We can now access the instance from the core module with ExampleAddon#chatExecutor()
. Now we can require an ExampleAddon
instance as a parameter in our ExamplePingCommand
and pass this
to the constructor of the command in the ExampleAddon#enable
. Then we can access the VersionedExampleChatExecutor
instance from the command and call the displayMessageInChat
method with "Pong!"
as an argument.
After starting LabyMod 4, joining a server, and executing "/pong", we'll see a colorless "Pong!". Now, if we want that
message colored, we need to replace String
in our interface with Component
. Instead of using Command.literal
, we
get the ComponentMapper
via Laby.labyApi().minecraft().componentMapper()
and call toMinecraftComponent(component)
.
Now our component from the Adventure Component library was mapped to a Minecraft Component, and
calling displayMessageInChat(Component.text("Pong!", NamedTextColor.GOLD))
in our Command works like before. Just with
our own implementation.
Those are the results from this example:
@Referenceable
public interface ExampleChatExecutor {
void displayMessageInChat(String message);
void displayMessageInChat(Component adventureComponent);
}
@Singleton
@Implements(ExampleChatExecutor.class)
public class VersionedExampleChatExecutor implements ExampleChatExecutor {
@Override
public void displayMessageInChat(String message) {
Component component = Component.literal(message);
this.addMessageToChat(component);
}
@Override
public void displayMessageInChat(net.kyori.adventure.text.Component adventureComponent) {
ComponentMapper componentMapper = Laby.labyAPI().minecraft().componentMapper();
Component component = (Component) componentMapper.toMinecraftComponent(adventureComponent);
Minecraft.getInstance().gui.getChat().addMessage(component);
}
private void addMessageToChat(Component component) {
Minecraft.getInstance().gui.getChat().addMessage(component);
}
}
public class ExampleAddon extends LabyAddon<ExampleConfiguration> {
private ExampleChatExecutor chatExecutor;
@Override
protected void enable() {
chatExecutor = ((DefaultReferenceStorage) this.referenceStorageAccessor()).exampleChatExecutor();
this.registerCommand(new ExamplePingCommand(this));
}
@Override
protected Class<? extends ExampleConfiguration> configurationClass() {
return ExampleConfiguration.class;
}
public ExampleChatExecutor chatExecutor() {
return chatExecutor;
}
public class ExamplePingCommand extends Command {
private final ExampleChatExecutor chatExecutor;
public ExamplePingCommand(ExampleAddon addon) {
super("ping", "pong");
chatExecutor = addon.chatExecutor();
}
@Override
public boolean execute(String prefix, String[] arguments) {
if (prefix.equalsIgnoreCase("ping")) {
this.displayMessage(Component.text("Ping!", NamedTextColor.AQUA));
return false;
}
chatExecutor.displayMessageInChat(Component.text("Pong!", NamedTextColor.GOLD));
return true;
}
}
Access the Minecraft Code via Mixin
Important Note
Please keep in mind that the moment your addon uses Mixins, it requires a restart when downloaded via the addon store.
todo: write
Inheriting Version Dependent Code
todo: write