# Plugin Tutorial: Discord Integration

Let's write a simple game server plugin to log player messages sent in-game to a Discord channel using a webhook. No prior knowledge is needed for this tutorial (obviously, beyond Java).

## Baby steps

Start by including the module containing the Irminsul API into your project.

```xml
    <dependency>
        <groupId>io.irminsul</groupId>
        <artifactId>common</artifactId>
        <version>1.0.0</version>
        <scope>provided</scope> 
    </dependency>
```

Create your main plugin class and extend the `GamePlugin` class located in `io.irminsul.common.plugin`. Implement the required `onEnable` and `onDisable` methods.&#x20;

Your plugin **must** have a public constructor that takes in no parameters. Java creates this implicitly if you have no other constructor.

```java
package dev.niqumu.discordplugin;

import io.irminsul.common.game.GameServer;

public class DiscordPlugin extends GamePlugin {
    
    /**
     * Called by the plugin manager when this plugin is enabled by the server
     */
    @Override
    public void onEnable() {
        this.logger.info("Plugin enabled!");
    }

    /**
     * Called by the plugin manager when this plugin is disabled by the server
     */
    @Override
    public void onDisable() {
        this.logger.info("Plugin disabled!");
    }
}
```

As you may have noticed in the `onEnable` and `onDisable` methods in this example, you have access to an SLF4J logger belonging to your plugin. You also have access to the `GameServer` to which your plugin is running on through the `this.server` field.

{% hint style="warning" %}
*When* exactly a plugin is instantiated, and what state the server is in at that time, is unspecified and up to the implementation. Our default game server makes no guarantees of safety or nullability at the time plugins are loaded. Place your logic in `onEnable`, **not** your constructor!
{% endhint %}

Next, we must create a `plugin.properties` file to provide some basic information about our plugin.

```properties
# Required. The internal ID of your plugin
id=discord-logging-plugin

# Optional, but highly recommended. The version of your plugin
version=1.0.0

# Optional, but highly recommended. The display/friendly name of your plugin
name=Discord Logger

# Optional, but highly recommended. A brief description of your plugin
description=Example plugin to demonstrate the Irminsul plugin API!

# Optional, but highly recommended. The author(s) of your plugin
author=Irminsul Team

# Optional. A website associated with your plugin, such as a Git repository
website=https://github.com/niqumu/Irminsul/

# Required. The main class of your plugin (whatever extends GamePlugin)
main=dev.niqumu.discordplugin.DiscordPlugin
```

{% hint style="info" %}
By convention, plugin IDs are all lowercase, with words separated by dashes.
{% endhint %}

{% hint style="info" %}
We strongly recommend following semantic versioning for your plugin version numbers! *(major.minor.patch)*
{% endhint %}

Let's try it out! Build your plugin using Maven via the Package task. Install the plugin using the steps shown in the [Plugin Installation tutorial](/irminsul/reference/plugin-installation-tutorial.md). Try running Irminsul and see what happens!

<figure><img src="/files/hdq4Me00u3slIFke57Ck" alt=""><figcaption><p>A screenshot of our plugin logging to the server console</p></figcaption></figure>

You can also try using the `plugins` command to verify that your plugin is loaded, and to see some information on it.

<figure><img src="/files/fNfmdcmeyZo0ZyYJ093X" alt=""><figcaption><p>Output of the plugins command, showing our plugin successfully loaded into the server. Nice!</p></figcaption></figure>

It works! It's not very interesting though. Let's make our plugin actually *do something*.

## Events

Most of your plugin logic will be based around subscribing to, and handling, events. The game server fires events for basically everything. You can subscribe to events, add your own logic on them, manipulate them, or even cancel them altogether.

Irminsul's event bus is simple. Register an object as an event subscriber, and methods annotated with `@EventHandler` will be called intelligently based on the event in the method parameters. Let's set up a basic handler for the `PlayerLoginEvent` event.

```java
@Override
public void onEnable() {
    this.registerEventSubscriber(this);
}

@EventHandler
public void onPlayerLogin(PlayerLoginEvent event) {
    this.logger.info("{} is logging in!", event.getPlayer().getProfile().getNickname());
}
```

Let's try it out!

<figure><img src="/files/2CZVXJG32dNPISQKSlog" alt=""><figcaption><p>A screenshot of our plugin logging when a player joins the server</p></figcaption></figure>

It works! We can get all sorts of information on the player joining through the event. Since `PlayerLoginEvent` does not extend `CancellableEvent`, we aren't able to cancel the player login, since it's not really something we want to cancel.

Let's add a second event handler for the `PlayerChatEvent` event and see what we can do with it.

```java
@EventHandler
public void onPlayerChat(PlayerChatEvent event) {
    this.logger.info("{} -> {}: {}", event.getSender(), event.getRecipient(), event.getText());
}
```

<figure><img src="/files/96zCi7A2SSp8bf7Rb6Di" alt=""><figcaption><p>A screenshot of our plugin logging in-game player messages</p></figcaption></figure>

This picture shows a player messaging the server console (UID 0). It works! Now, let's use a simple Discord webhook library to achieve our goal. If you aren't familiar with one, check out k3kdude's [simple one-class webhook wrapper](https://gist.github.com/k3kdude/fba6f6b37594eae3d6f9475330733bdb).

```java
@EventHandler
public void onPlayerChat(PlayerChatEvent event) {
    DiscordWebhook webhook = new DiscordWebhook(webhookUrl);
    DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject();

    webhook.setUsername("Plugin");
    embed.setTitle("%s -> %d".formatted(event.getSender().toString(), event.getRecipient()));
    embed.setDescription(event.getEmote() != 0 ? ("(emote " + event.getEmote() + ")") : event.getText());
    embed.setColor(Color.CYAN);
    embed.setFooter("Sent from %s %s".formatted(this.getPluginInfo().getName(), this.getPluginInfo().getVersion()), "");

    try {
        webhook.addEmbed(embed);
        webhook.execute();
    } catch (IOException e) {
        this.logger.error("Couldn't send message to webhook!", e);
    }
}
```

Let's try it out and send some messages to the server console!

<figure><img src="/files/wAQ48P73oGVEqQyZMJ3h" alt=""><figcaption><p>A Discord screenshot of our plugin sending messages to a webhook. Success!</p></figcaption></figure>

It works! We did it! We wrote a simple plugin to log player chat messages to a Discord webhook. We've began to explore the potentials of the Irminsul plugin API.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://irminsul.gitbook.io/irminsul/api/api-tutorials/plugin-tutorial-discord-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
