Minecraft (Paper)
Integrate ServerOps with your Paper Minecraft server for logging and media uploads.
Minecraft (Paper): Getting started
This guide walks you through creating a simple Paper plugin that sends player events to ServerOps. You will need basic Java knowledge, but no advanced programming experience.
Before you start
- Complete Getting your API token. You need
logs:writeand/ormedia:writescope. - Your server must be running Paper 1.20+ (Java 21 required).
- You will need a way to compile a Java plugin (IntelliJ IDEA Community is free and works well).
Plugin setup
1. Create a new Paper plugin project
The quickest way is to use the Paper plugin generator. Fill in:
- Group: your domain reversed, e.g.
gg.serverops - Artifact:
ServerOpsPlugin - Paper version: select the latest
Download the generated project and open it in IntelliJ IDEA.
2. Add your token to config.yml
Open src/main/resources/config.yml and add:
serverops:
api-token: "so_live_..."3. Create the API client class
Create src/main/java/gg/serverops/plugin/ServerOpsClient.java:
package gg.serverops.plugin;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;
import com.google.gson.Gson;
import org.bukkit.plugin.java.JavaPlugin;
public class ServerOpsClient {
private final HttpClient httpClient = HttpClient.newHttpClient();
private final Gson gson = new Gson();
private final JavaPlugin plugin;
public ServerOpsClient(JavaPlugin plugin) {
this.plugin = plugin;
}
private String getToken() {
return plugin.getConfig().getString("serverops.api-token", "");
}
public void sendLog(String level, String message, Map<String, Object> meta) {
String token = getToken();
if (token.isEmpty()) {
plugin.getLogger().warning("[ServerOps] api-token is not set in config.yml");
return;
}
String body = gson.toJson(Map.of(
"level", level,
"message", message,
"meta", meta != null ? meta : Map.of()
));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.serverops.gg/v1/logs"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> {
if (response.statusCode() != 201) {
plugin.getLogger().warning(
"[ServerOps] Log failed (" + response.statusCode() + "): " + response.body()
);
}
});
}
}4. Wire up your main plugin class
Open your main plugin class (the one that extends JavaPlugin) and update it:
package gg.serverops.plugin;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Map;
public class ServerOpsPlugin extends JavaPlugin implements Listener {
private ServerOpsClient client;
@Override
public void onEnable() {
saveDefaultConfig();
client = new ServerOpsClient(this);
getServer().getPluginManager().registerEvents(this, this);
getLogger().info("ServerOps integration enabled.");
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
var player = event.getPlayer();
client.sendLog("info", "Player joined", Map.of(
"name", player.getName(),
"uuid", player.getUniqueId().toString(),
"address", player.getAddress() != null ? player.getAddress().getHostString() : "unknown"
));
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
var player = event.getPlayer();
client.sendLog("info", "Player left", Map.of(
"name", player.getName(),
"uuid", player.getUniqueId().toString()
));
}
}5. Build and install
Run ./gradlew build (or mvn package if using Maven). Copy the generated .jar from build/libs/ into your server's plugins/ folder. Restart the server.
Edit plugins/ServerOpsPlugin/config.yml to set your API token:
serverops:
api-token: "so_live_..."Reload the config with /reload confirm or restart the server.
Adding ban logs
Hook into the PlayerKickEvent or use a ban plugin's API to log bans:
@EventHandler
public void onPlayerKick(PlayerKickEvent event) {
client.sendLog("warn", "Player kicked", Map.of(
"name", event.getPlayer().getName(),
"reason", event.reason().toString()
));
}Viewing your logs
Logs appear in your ServerOps dashboard as they arrive.
Troubleshooting
"api-token is not set": Open plugins/ServerOpsPlugin/config.yml and add your token under serverops.api-token.
401 Unauthorized: Your token is invalid or missing the logs:write scope. Generate a new token from the dashboard.
Plugin does not load: Check your Paper version. This plugin requires Paper 1.20+ with Java 21. Run java -version on your server to verify.