Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# LiveMotdManager

LiveMotdManager is a multi-platform Minecraft plugin providing a dynamic server list MOTD. It supports Spigot/Paper/Purpur/Folia servers and BungeeCord/Waterfall (1.16+) / Velocity proxies. The plugin changes MOTD based on time, player count, TPS and more. Weather and Discord integrations are included.
LiveMotdManager is a multi-platform Minecraft plugin that keeps your server list message dynamic.
It supports Spigot/Paper/Purpur/Folia servers and BungeeCord/Waterfall (1.16+) or Velocity proxies.
The MOTD can react to time of day, player counts, TPS, real world weather and Discord activity.

## Building

Expand All @@ -10,31 +12,39 @@ Requirements: Java 17+ and Maven.
mvn package
```

Resulting jars will be in `spigot/target`, `bungee/target` and `velocity/target`.
Resulting jars are placed in `spigot/target`, `bungee/target` and `velocity/target` named like
`livemotdmanager-spigot-1.0.0.jar`.

## Installation

1. Place the jar for your platform into the plugins folder.
2. Start the server/proxy to generate the config file.
3. Edit `config.yml` as needed and run `/motd reload` to apply changes.
2. Start the server/proxy to generate `config.yml`.
3. Edit the configuration and run `/motd reload` to apply changes.

## Commands

Run `/motd help` in game for usage.

- `/motd reload` – reload configuration.
- `/motd set <text>` – set temporary MOTD until restart.
- `/motd info` – show active template and debug info.
- `/motd force <template|off>` – force a configured template regardless of conditions.

## Configuration

See `config.yml` for an example configuration with multiple templates and integrations.

## Weather

Uses [open-meteo.com](https://open-meteo.com/) APIs with no key required.
Uses [open-meteo.com](https://open-meteo.com/) APIs with no key required. The plugin performs an
initial API test on startup and logs the result to the console.

## Discord

Optional integration with DiscordSRV. Placeholder `%discord_online%` shows number of connected Discord users.
Optional integration with DiscordSRV. Placeholder `%discord_online%` shows number of connected
Discord users.

Additional documentation is available in the [wiki](wiki/Home.md).

## License

Expand Down
5 changes: 3 additions & 2 deletions bungee/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<parent>
<groupId>com.livemotdmanager</groupId>
<artifactId>livemotdmanager-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</parent>
<artifactId>bungee</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.livemotdmanager</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
Expand All @@ -22,6 +22,7 @@
</dependency>
</dependencies>
<build>
<finalName>livemotdmanager-bungee-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,16 @@ public class MotdCommand extends Command {
@Override
public void execute(CommandSender sender, String[] args) {
if (args.length == 0) {
sender.sendMessage(new TextComponent("/motd reload|set|info"));
sender.sendMessage(new TextComponent("/motd help"));
return;
}
switch (args[0].toLowerCase()) {
case "help":
sender.sendMessage(new TextComponent("/motd reload - reload configuration"));
sender.sendMessage(new TextComponent("/motd set <text> - set temporary MOTD"));
sender.sendMessage(new TextComponent("/motd info - show debug info"));
sender.sendMessage(new TextComponent("/motd force <template|off> - force a template"));
break;
case "reload":
loadConfig();
sender.sendMessage(new TextComponent("Config reloaded."));
Expand All @@ -129,8 +135,21 @@ public void execute(CommandSender sender, String[] args) {
sender.sendMessage(new TextComponent("Weather: " + weather.getCachedWeather()));
sender.sendMessage(new TextComponent("Discord online: " + discord.getOnlineUsers()));
break;
case "force":
if (args.length < 2) {
sender.sendMessage(new TextComponent("Usage: /motd force <template|off>"));
break;
}
if (args[1].equalsIgnoreCase("off")) {
manager.clearForcedTemplate();
sender.sendMessage(new TextComponent("Forced template cleared."));
} else {
manager.setForcedTemplate(args[1]);
sender.sendMessage(new TextComponent("Forced template set to " + args[1] + "."));
}
break;
default:
sender.sendMessage(new TextComponent("Unknown subcommand."));
sender.sendMessage(new TextComponent("Unknown subcommand. Use /motd help"));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>com.livemotdmanager</groupId>
<artifactId>livemotdmanager-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</parent>
<artifactId>core</artifactId>
<dependencies>
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/java/com/livemotdmanager/core/MotdManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class MotdManager {
private String currentTemplate = "";
private final long cacheIntervalMs;
private String temporaryMotd = null;
private String forcedTemplate = null;

public MotdManager(MotdConfig config, WeatherService weather, DiscordService discord, ServerInfoProvider infoProvider) {
this.config = config;
Expand All @@ -48,6 +49,15 @@ public Component provide() {
if (temporaryMotd != null) {
return MiniMessage.miniMessage().deserialize(applyPlaceholders(temporaryMotd, ctx));
}
if (forcedTemplate != null) {
for (MotdConfig.MotdRule rule : config.motd) {
if (rule.when.equalsIgnoreCase(forcedTemplate)) {
currentTemplate = rule.when;
String txt = applyPlaceholders(rule.text, ctx);
return MiniMessage.miniMessage().deserialize(txt);
}
}
}
if (now - lastUpdate > cacheIntervalMs) {
rebuild(ctx);
lastUpdate = now;
Expand Down Expand Up @@ -78,6 +88,14 @@ public String applyPlaceholders(String text, MotdContext ctx) {
return out;
}

public void setForcedTemplate(String when) {
this.forcedTemplate = when;
}

public void clearForcedTemplate() {
this.forcedTemplate = null;
}

private String defaultPlaceholders(String text, MotdContext ctx) {
String out = text;
out = out.replace("%online%", Integer.toString(ctx.online));
Expand Down
13 changes: 9 additions & 4 deletions core/src/main/java/com/livemotdmanager/core/WeatherService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import com.google.gson.JsonParser;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
Expand All @@ -32,7 +33,10 @@ public WeatherService(MotdConfig.WeatherSettings settings) {

public void start() {
if (!enabled) return;
scheduler.scheduleAtFixedRate(this::update, 0, updateIntervalMinutes, TimeUnit.MINUTES);
// Initial fetch for startup verification
update();
System.out.println("[LiveMotdManager] Weather API test: " + (cached.isEmpty() ? "unavailable" : cached));
scheduler.scheduleAtFixedRate(this::update, updateIntervalMinutes, updateIntervalMinutes, TimeUnit.MINUTES);
}

public void stop() {
Expand All @@ -46,8 +50,9 @@ public String getCachedWeather() {
private void update() {
if (!enabled || city == null || city.isEmpty()) return;
try {
// Geocode city
String geocodeUrl = String.format("https://geocoding-api.open-meteo.com/v1/search?count=1&name=%s", city);
// Geocode city (encode to handle spaces and special characters)
String encodedCity = URLEncoder.encode(city, StandardCharsets.UTF_8);
String geocodeUrl = String.format("https://geocoding-api.open-meteo.com/v1/search?count=1&name=%s", encodedCity);
HttpRequest geoReq = HttpRequest.newBuilder().uri(URI.create(geocodeUrl)).timeout(Duration.ofSeconds(10)).build();
HttpResponse<String> geoResp = http.send(geoReq, HttpResponse.BodyHandlers.ofString());
JsonObject geoJson = JsonParser.parseString(geoResp.body()).getAsJsonObject();
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.livemotdmanager</groupId>
<artifactId>livemotdmanager-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
Expand Down
5 changes: 3 additions & 2 deletions spigot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<parent>
<groupId>com.livemotdmanager</groupId>
<artifactId>livemotdmanager-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</parent>
<artifactId>spigot</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.livemotdmanager</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
Expand All @@ -26,6 +26,7 @@
</dependency>
</dependencies>
<build>
<finalName>livemotdmanager-spigot-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,16 @@ public double tps() {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
sender.sendMessage("/motd reload|set|info");
sender.sendMessage("/motd help");
return true;
}
switch (args[0].toLowerCase()) {
case "help":
sender.sendMessage("/motd reload - reload configuration");
sender.sendMessage("/motd set <text> - set temporary MOTD");
sender.sendMessage("/motd info - show debug info");
sender.sendMessage("/motd force <template|off> - force a template");
break;
case "reload":
loadConfig();
sender.sendMessage("MOTD config reloaded.");
Expand All @@ -138,15 +144,28 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
sender.sendMessage("Weather: " + weather.getCachedWeather());
sender.sendMessage("Discord online: " + discord.getOnlineUsers());
break;
case "force":
if (args.length < 2) {
sender.sendMessage("Usage: /motd force <template|off>");
break;
}
if (args[1].equalsIgnoreCase("off")) {
manager.clearForcedTemplate();
sender.sendMessage("Forced template cleared.");
} else {
manager.setForcedTemplate(args[1]);
sender.sendMessage("Forced template set to " + args[1] + ".");
}
break;
default:
sender.sendMessage("Unknown subcommand.");
sender.sendMessage("Unknown subcommand. Use /motd help");
}
return true;
}

@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (args.length == 1) return Arrays.asList("reload", "set", "info");
if (args.length == 1) return Arrays.asList("help", "reload", "set", "info", "force");
return List.of();
}
}
5 changes: 3 additions & 2 deletions velocity/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<parent>
<groupId>com.livemotdmanager</groupId>
<artifactId>livemotdmanager-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</parent>
<artifactId>velocity</artifactId>
<dependencies>
<dependency>
<groupId>com.livemotdmanager</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
Expand All @@ -20,6 +20,7 @@
</dependency>
</dependencies>
<build>
<finalName>livemotdmanager-velocity-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,16 @@ public class MotdCommand implements SimpleCommand {
public void execute(Invocation invocation) {
String[] args = invocation.arguments();
if (args.length == 0) {
invocation.source().sendMessage(Component.text("/motd reload|set|info"));
invocation.source().sendMessage(Component.text("/motd help"));
return;
}
switch (args[0].toLowerCase()) {
case "help":
invocation.source().sendMessage(Component.text("/motd reload - reload configuration"));
invocation.source().sendMessage(Component.text("/motd set <text> - set temporary MOTD"));
invocation.source().sendMessage(Component.text("/motd info - show debug info"));
invocation.source().sendMessage(Component.text("/motd force <template|off> - force a template"));
break;
case "reload":
loadConfig();
invocation.source().sendMessage(Component.text("Config reloaded."));
Expand All @@ -122,8 +128,21 @@ public void execute(Invocation invocation) {
invocation.source().sendMessage(Component.text("Weather: " + weather.getCachedWeather()));
invocation.source().sendMessage(Component.text("Discord online: " + discord.getOnlineUsers()));
break;
case "force":
if (args.length < 2) {
invocation.source().sendMessage(Component.text("Usage: /motd force <template|off>"));
break;
}
if (args[1].equalsIgnoreCase("off")) {
manager.clearForcedTemplate();
invocation.source().sendMessage(Component.text("Forced template cleared."));
} else {
manager.setForcedTemplate(args[1]);
invocation.source().sendMessage(Component.text("Forced template set to " + args[1] + "."));
}
break;
default:
invocation.source().sendMessage(Component.text("Unknown subcommand."));
invocation.source().sendMessage(Component.text("Unknown subcommand. Use /motd help"));
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions wiki/Home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# LiveMotdManager Wiki

Welcome to the LiveMotdManager wiki. This documentation expands on configuration and usage.

## Features

- Dynamic MOTD templates based on time, player count and TPS
- Real world weather integration via [open-meteo.com](https://open-meteo.com/)
- DiscordSRV integration
- Works on Spigot/Paper/Folia servers and BungeeCord/Waterfall/Velocity proxies

## Configuration

See the included `config.yml` for a starting point. Each `motd` entry contains a `when` rule
and the MiniMessage formatted `text` to display.

### Weather

Set `weather.enable` to `true` and specify `city`. Use `%weather_city%` or `%weather_<city>%`
placeholders in your templates.

### Discord

If DiscordSRV is installed you can show online Discord users with `%discord_online%`.

## Commands

Run `/motd help` in game for the complete list.

- `/motd reload` – reload configuration
- `/motd set <text>` – set a temporary MOTD until restart
- `/motd info` – show debug information
- `/motd force <template|off>` – force a configured template

## Building

Run `mvn package` to build. Jars are created in each module's `target` directory with names like
`livemotdmanager-spigot-1.0.0.jar`.

Loading