From a795d00f08f17a999a11ac6f6784286fbfe2f5d9 Mon Sep 17 00:00:00 2001 From: MC20018 Date: Sat, 4 Jul 2026 01:10:29 +0800 Subject: [PATCH 1/2] Add Fand platform support --- README.md | 1 + platform/fand/build.gradle.kts | 36 +++++ .../jpenilla/minimotd/fand/MiniMOTDFand.java | 129 ++++++++++++++++++ settings.gradle.kts | 2 + 4 files changed, 168 insertions(+) create mode 100644 platform/fand/build.gradle.kts create mode 100644 platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java diff --git a/README.md b/README.md index 7735cbb3..c4c018f6 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [Sponge API 7](https://www.spongepowered.org/) - [Fabric](https://fabricmc.net/) (requires [Fabric API](https://modrinth.com/mod/fabric-api)) - [NeoForge](https://neoforged.net/) +- [Fand](https://github.com/FandMC/Fand) #### Proxy Platforms - [Velocity](https://velocitypowered.com/) diff --git a/platform/fand/build.gradle.kts b/platform/fand/build.gradle.kts new file mode 100644 index 00000000..4659f6be --- /dev/null +++ b/platform/fand/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id("minimotd.shadow-platform") + id("io.fand.plugin") version "latest.release" +} + +dependencies { + implementation(projects.minimotdCommon) + compileOnly(libs.slf4jApi) +} + +fandPlugin { + id.set("minimotd") + version.set(project.version.toString()) + mainClass.set("xyz.jpenilla.minimotd.fand.MiniMOTDFand") + description.set(project.description ?: "") + website.set(Constants.GITHUB_URL) + license.set("MIT") + apiVersion.set("0.1.1") + authors.add("jmp") + directRunGuard.set(false) +} + +tasks { + named("sourcesJar") { + dependsOn("generateFandPluginDescriptor") + } + shadowJar { + dependencies { + exclude { dependency -> dependency.moduleGroup == "io.fand" } + exclude { dependency -> dependency.moduleGroup == "net.kyori" } + exclude { dependency -> dependency.moduleGroup == "org.slf4j" } + exclude { dependency -> dependency.moduleGroup == "org.jspecify" } + exclude { dependency -> dependency.moduleGroup == "com.google.code.gson" } + } + } +} diff --git a/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java b/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java new file mode 100644 index 00000000..8226fe17 --- /dev/null +++ b/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java @@ -0,0 +1,129 @@ +/* + * This file is part of MiniMOTD, licensed under the MIT License. + * + * Copyright (c) 2020-2025 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package xyz.jpenilla.minimotd.fand; + +import io.fand.api.Fand; +import io.fand.api.command.CommandDescriptor; +import io.fand.api.command.CommandSender; +import io.fand.api.event.server.ServerListIcon; +import io.fand.api.event.server.ServerListPingEvent; +import io.fand.api.plugin.Plugin; +import io.fand.api.plugin.PluginContext; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; +import javax.imageio.ImageIO; +import org.jspecify.annotations.NonNull; +import org.slf4j.Logger; +import xyz.jpenilla.minimotd.common.CommandHandler; +import xyz.jpenilla.minimotd.common.MiniMOTD; +import xyz.jpenilla.minimotd.common.MiniMOTDPlatform; +import xyz.jpenilla.minimotd.common.PingResponse; +import xyz.jpenilla.minimotd.common.config.MOTDConfig; + +public final class MiniMOTDFand implements Plugin, MiniMOTDPlatform { + private static final List COMMANDS = List.of("about", "reload", "help"); + + private PluginContext context; + private MiniMOTD miniMOTD; + private CommandHandler commandHandler; + + @Override + public void onEnable(final PluginContext context) { + this.context = context; + this.miniMOTD = new MiniMOTD<>(this); + this.commandHandler = new CommandHandler(this.miniMOTD); + + context.commands().register( + new CommandDescriptor("minimotd", "minimotd", COMMANDS, List.of(), "minimotd.admin"), + (sender, label, args) -> this.executeCommand(args, sender), + (sender, label, args) -> this.completeCommand(args) + ); + context.events().subscribe(ServerListPingEvent.class, this::handlePing); + } + + @Override + public @NonNull Path dataDirectory() { + return this.context.dataDirectory(); + } + + @Override + public @NonNull Logger logger() { + return this.context.logger(); + } + + @Override + public @NonNull ServerListIcon loadIcon(final @NonNull BufferedImage image) throws Exception { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + ImageIO.write(image, "png", output); + return new ServerListIcon(output.toByteArray()); + } + + @Override + public void onReload() { + Fand.server().refreshServerListStatus(); + } + + private void executeCommand(final List args, final CommandSender sender) { + final String subcommand = args.isEmpty() ? "help" : args.get(0).toLowerCase(Locale.ROOT); + switch (subcommand) { + case "about" -> this.commandHandler.about(sender); + case "reload" -> this.commandHandler.reload(sender); + case "help" -> this.commandHandler.help(sender); + default -> this.commandHandler.help(sender); + } + } + + private List completeCommand(final List args) { + if (args.size() != 1) { + return List.of(); + } + final String prefix = args.get(0).toLowerCase(Locale.ROOT); + return COMMANDS.stream() + .filter(option -> option.startsWith(prefix)) + .toList(); + } + + private void handlePing(final ServerListPingEvent event) { + final MOTDConfig config = this.miniMOTD.configManager().mainConfig(); + final PingResponse response = this.miniMOTD.createMOTD( + config, + event.onlinePlayers(), + event.maxPlayers() + ); + + response.playerCount().applyCount(event::setOnlinePlayers, event::setMaxPlayers); + response.motd(event::setMotd); + response.icon(event::setIcon); + + if (response.disablePlayerListHover()) { + event.setSamplePlayers(List.of()); + } + if (response.hidePlayerCount()) { + event.setHidePlayers(true); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index c17373a7..4fe7be51 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { repositories { + maven("https://repo.fandmc.cn/repository/maven-public/") gradlePluginPortal() maven("https://maven.fabricmc.net/") maven("https://maven.neoforged.net/releases/") { @@ -38,4 +39,5 @@ sequenceOf( "velocity", "fabric", "neoforge", + "fand", ).forEach(::platform) From 2496a065f4d2e074a03bf5ca665100c22c5052e3 Mon Sep 17 00:00:00 2001 From: MC20018 Date: Sun, 5 Jul 2026 12:41:01 +0800 Subject: [PATCH 2/2] Update Fand command registration --- .../jpenilla/minimotd/fand/MiniMOTDFand.java | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java b/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java index 8226fe17..cdc542e2 100644 --- a/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java +++ b/platform/fand/src/main/java/xyz/jpenilla/minimotd/fand/MiniMOTDFand.java @@ -24,8 +24,6 @@ package xyz.jpenilla.minimotd.fand; import io.fand.api.Fand; -import io.fand.api.command.CommandDescriptor; -import io.fand.api.command.CommandSender; import io.fand.api.event.server.ServerListIcon; import io.fand.api.event.server.ServerListPingEvent; import io.fand.api.plugin.Plugin; @@ -34,7 +32,6 @@ import java.io.ByteArrayOutputStream; import java.nio.file.Path; import java.util.List; -import java.util.Locale; import javax.imageio.ImageIO; import org.jspecify.annotations.NonNull; import org.slf4j.Logger; @@ -45,8 +42,6 @@ import xyz.jpenilla.minimotd.common.config.MOTDConfig; public final class MiniMOTDFand implements Plugin, MiniMOTDPlatform { - private static final List COMMANDS = List.of("about", "reload", "help"); - private PluginContext context; private MiniMOTD miniMOTD; private CommandHandler commandHandler; @@ -57,11 +52,12 @@ public void onEnable(final PluginContext context) { this.miniMOTD = new MiniMOTD<>(this); this.commandHandler = new CommandHandler(this.miniMOTD); - context.commands().register( - new CommandDescriptor("minimotd", "minimotd", COMMANDS, List.of(), "minimotd.admin"), - (sender, label, args) -> this.executeCommand(args, sender), - (sender, label, args) -> this.completeCommand(args) - ); + context.commands().register("minimotd", command -> command + .permission("minimotd.admin") + .executes(commandContext -> this.commandHandler.help(commandContext.sender())) + .literal("about", about -> about.executes(commandContext -> this.commandHandler.about(commandContext.sender()))) + .literal("reload", reload -> reload.executes(commandContext -> this.commandHandler.reload(commandContext.sender()))) + .literal("help", help -> help.executes(commandContext -> this.commandHandler.help(commandContext.sender())))); context.events().subscribe(ServerListPingEvent.class, this::handlePing); } @@ -87,26 +83,6 @@ public void onReload() { Fand.server().refreshServerListStatus(); } - private void executeCommand(final List args, final CommandSender sender) { - final String subcommand = args.isEmpty() ? "help" : args.get(0).toLowerCase(Locale.ROOT); - switch (subcommand) { - case "about" -> this.commandHandler.about(sender); - case "reload" -> this.commandHandler.reload(sender); - case "help" -> this.commandHandler.help(sender); - default -> this.commandHandler.help(sender); - } - } - - private List completeCommand(final List args) { - if (args.size() != 1) { - return List.of(); - } - final String prefix = args.get(0).toLowerCase(Locale.ROOT); - return COMMANDS.stream() - .filter(option -> option.startsWith(prefix)) - .toList(); - } - private void handlePing(final ServerListPingEvent event) { final MOTDConfig config = this.miniMOTD.configManager().mainConfig(); final PingResponse response = this.miniMOTD.createMOTD(