diff --git a/README.md b/README.md index 4360d91..1e4ce36 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,12 @@ Messages can be sent to any audience, such as players or the console. ## Supported Platforms -| Platform | Module | Java Version | Adventure API | Status | -|-------------------|------------------------|--------------|-----------------------|------------------------| -| **Paper** | `multification-paper` | Java 21 | Native (built-in) | ✅ Recommended | -| **Bukkit/Spigot** | `multification-bukkit` | Java 8+ | External adapter | ✅ Supported | -| **Velocity** | `multification-velocity` | Java 21+ | Native | ❌ Soon | -| **Core** | `multification-core` | Java 8+ | Custom implementation | ✅ For custom platforms | +| Platform | Module | Java Version | Adventure API | Status | +|-------------------|--------------------------|--------------|-----------------------|-------------------------| +| **Paper** | `multification-paper` | Java 21 | Native (built-in) | ✅ Supported | +| **Bukkit/Spigot** | `multification-bukkit` | Java 8+ | External adapter | ✅ Supported | +| **Velocity** | `multification-velocity` | Java 21+ | Native | ✅ Supported | +| **Core** | `multification-core` | Java 8+ | Custom implementation | 🔨 For custom platforms | > **💡 Recommendation:** Use `multification-paper` for Paper servers (1.19.4+) to leverage native Adventure API without > external dependencies. @@ -175,9 +175,9 @@ public class MessagesConfig { ```java Cdn cdn = CdnFactory.createYamlLike() - .getSettings() - .withComposer(Notice.class, new MultificationNoticeCdnComposer(multification.getNoticeRegistry())) - .build(); + .getSettings() + .withComposer(Notice.class, new MultificationNoticeCdnComposer(multification.getNoticeRegistry())) + .build(); ``` #### (CDN) 4. Load the configuration: @@ -227,7 +227,10 @@ public class MessagesConfig extends OkaeriConfig { ```java MessagesConfig config = (MessagesConfig) ConfigManager.create(MessagesConfig.class) .withConfigurer(new MultificationSerdesPack(multification.getNoticeRegistry())) - .withConfigurer(new SerdesCommons(), new YamlBukkitConfigurer(), new SerdesBukkit()) // specify configurers for your platform + .withConfigurer( + new SerdesCommons(), + new YamlBukkitConfigurer(), + new SerdesBukkit()) // specify configurers for your platform .withBindFile(new File(dataFolder, "messages.yml")) .withRemoveOrphans(true) // automatic removal of undeclared keys .saveDefaults() // save file if does not exists diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index cc97647..f6441c7 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,4 +1,3 @@ - object Versions { const val ADVENTURE_PLATFORM_BUKKIT = "4.3.3" @@ -11,7 +10,9 @@ object Versions { const val AWAITILITY = "4.3.0" const val SPIGOT_API = "1.21.4-R0.1-SNAPSHOT" + const val VELOCITY_API = "3.4.0-SNAPSHOT" const val JETBRAINS_ANNOTATIONS = "26.0.2-1" + } diff --git a/examples/velocity/build.gradle.kts b/examples/velocity/build.gradle.kts new file mode 100644 index 0000000..29badf1 --- /dev/null +++ b/examples/velocity/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("java") + id("com.gradleup.shadow") version "9.0.0-beta4" + id("xyz.jpenilla.run-velocity") version "3.0.2" +} + +version = "1.0.0-SNAPSHOT" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.panda-lang.org/releases/") +} + +dependencies { + compileOnly("com.velocitypowered:velocity-api:${Versions.VELOCITY_API}") + annotationProcessor("com.velocitypowered:velocity-api:${Versions.VELOCITY_API}") + + implementation("dev.rollczi:litecommands-velocity:3.10.6") + // implementation("com.eternalcode:multification-velocity:1.2.3") // <-- uncomment in your project + // implementation("com.eternalcode:multification-cdn:1.2.3") // <-- uncomment in your project + + implementation(project(":multification-velocity")) // don't use this line in your build.gradle + implementation(project(":multification-cdn")) // don't use this line in your build.gradle +} + +val pluginName = "ExampleVelocityPlugin" +val packageName = "com.eternalcode.example.velocity" + +tasks.shadowJar { + archiveFileName.set("$pluginName v${project.version}.jar") + + listOf( + "dev.rollczi.litecommands", + "panda.std", + "panda.utilities", + ).forEach { relocate(it, "$packageName.libs.$it") } +} + +sourceSets.test { + java.setSrcDirs(emptyList()) + resources.setSrcDirs(emptyList()) +} + +tasks.runVelocity { + velocityVersion("${Versions.VELOCITY_API}") +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/ExampleVelocityPlugin.java b/examples/velocity/src/main/java/com/eternalcode/example/ExampleVelocityPlugin.java new file mode 100644 index 0000000..0323006 --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/ExampleVelocityPlugin.java @@ -0,0 +1,65 @@ +package com.eternalcode.example; + +import com.eternalcode.example.command.ReloadCommand; +import com.eternalcode.example.command.SwitchCommand; +import com.eternalcode.example.config.ConfigurationManager; +import com.eternalcode.example.config.MessagesConfig; +import com.eternalcode.example.notice.ExampleMultification; +import com.google.inject.Inject; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ProxyServer; +import dev.rollczi.litecommands.LiteCommands; +import dev.rollczi.litecommands.velocity.LiteVelocityFactory; + +import java.nio.file.Path; + +@Plugin( + id = "example-velocity-plugin", + name = "Example Velocity Plugin", + version = "1.0.0", + description = "An example Velocity plugin demonstrating Multification usage", + authors = { "EternalCode" } +) +public class ExampleVelocityPlugin { + + private final ProxyServer server; + private final Path dataDirectory; + + private LiteCommands liteCommands; + + @Inject + public ExampleVelocityPlugin(ProxyServer server, @DataDirectory Path dataDirectory) { + this.server = server; + this.dataDirectory = dataDirectory; + } + + @Subscribe + void onProxyInitialize(ProxyInitializeEvent event) { + MessagesConfig messagesConfig = new MessagesConfig(); + ExampleMultification multification = new ExampleMultification(this, this.server, messagesConfig); + + ConfigurationManager configurationManager = new ConfigurationManager(this.dataDirectory.toFile(), + multification.getNoticeRegistry()); + configurationManager.load(messagesConfig, "messages.yml"); + + this.liteCommands = LiteVelocityFactory.builder(this.server) + .commands( + new ReloadCommand(configurationManager, multification), + new SwitchCommand(multification)) + .build(); + + this.server.getEventManager().register(this, new PlayerConnectListener(multification)); + } + + @Subscribe + void onProxyShutdown(ProxyShutdownEvent event) { + if (this.liteCommands != null) { + this.liteCommands.unregister(); + } + } +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/PlayerConnectListener.java b/examples/velocity/src/main/java/com/eternalcode/example/PlayerConnectListener.java new file mode 100644 index 0000000..f0f0b60 --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/PlayerConnectListener.java @@ -0,0 +1,24 @@ +package com.eternalcode.example; + +import com.eternalcode.example.notice.ExampleMultification; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.player.ServerConnectedEvent; + +public class PlayerConnectListener { + + private final ExampleMultification multification; + + public PlayerConnectListener(ExampleMultification multification) { + this.multification = multification; + } + + @Subscribe + void onPlayerConnect(ServerConnectedEvent event) { + this.multification.create() + .all() + .notice(messagesConfig -> messagesConfig.joinedTheServer) + .placeholder("", event.getServer().getServerInfo().getName()) + .send(); + } + +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/command/ReloadCommand.java b/examples/velocity/src/main/java/com/eternalcode/example/command/ReloadCommand.java new file mode 100644 index 0000000..bed85ce --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/command/ReloadCommand.java @@ -0,0 +1,39 @@ +package com.eternalcode.example.command; + +import com.eternalcode.example.config.ConfigurationManager; +import com.eternalcode.example.notice.ExampleMultification; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; + +@Command(name = "reload-config") +public class ReloadCommand { + + private final ConfigurationManager configurationManager; + private final ExampleMultification multification; + + public ReloadCommand(ConfigurationManager configurationManager, ExampleMultification multification) { + this.configurationManager = configurationManager; + this.multification = multification; + } + + @Execute + public void execute(@Context CommandSource sender) { + this.configurationManager.reload(); + + if (sender instanceof Player player) { + this.multification.create() + .player(player.getUniqueId()) + .notice(messagesConfig -> messagesConfig.reloadMessage) + .send(); + return; + } + + this.multification.create() + .console() + .notice(messagesConfig -> messagesConfig.reloadMessage) + .send(); + } +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/command/SwitchCommand.java b/examples/velocity/src/main/java/com/eternalcode/example/command/SwitchCommand.java new file mode 100644 index 0000000..458c2cf --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/command/SwitchCommand.java @@ -0,0 +1,30 @@ +package com.eternalcode.example.command; + +import com.eternalcode.example.notice.ExampleMultification; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; + +@Command(name = "switch") +public class SwitchCommand { + + private final ExampleMultification multification; + + public SwitchCommand(ExampleMultification multification) { + this.multification = multification; + } + + @Execute + void execute(@Context Player player, @Arg RegisteredServer server) { + player.createConnectionRequest(server).fireAndForget(); + this.multification.create() + .player(player.getUniqueId()) + .notice(messagesConfig -> messagesConfig.switchedServer) + .placeholder("", server.getServerInfo().getName()) + .send(); + + } +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/config/ConfigurationManager.java b/examples/velocity/src/main/java/com/eternalcode/example/config/ConfigurationManager.java new file mode 100644 index 0000000..6086f9d --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/config/ConfigurationManager.java @@ -0,0 +1,42 @@ +package com.eternalcode.example.config; + +import com.eternalcode.multification.cdn.MultificationNoticeCdnComposer; +import com.eternalcode.multification.notice.Notice; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import net.dzikoysk.cdn.Cdn; +import net.dzikoysk.cdn.CdnFactory; +import net.dzikoysk.cdn.source.Source; + +import java.io.File; + +public class ConfigurationManager { + + private final File dataFolder; + private final Cdn cdn; + private MessagesConfig messagesConfig; + + public ConfigurationManager(File dataFolder, NoticeResolverRegistry noticeRegistry) { + this.dataFolder = dataFolder; + this.cdn = CdnFactory.createYamlLike() + .getSettings() + .withComposer(Notice.class, new MultificationNoticeCdnComposer(noticeRegistry)) + .build(); + } + + public void load(MessagesConfig config, String fileName) { + this.messagesConfig = config; + File file = new File(this.dataFolder, fileName); + + this.cdn.load(Source.of(file), config) + .orThrow(cause -> cause); + + this.cdn.render(config, Source.of(file)) + .orThrow(cause -> cause); + } + + public void reload() { + if (this.messagesConfig != null) { + load(this.messagesConfig, "messages.yml"); + } + } +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/config/MessagesConfig.java b/examples/velocity/src/main/java/com/eternalcode/example/config/MessagesConfig.java new file mode 100644 index 0000000..b4e7a1f --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/config/MessagesConfig.java @@ -0,0 +1,34 @@ +package com.eternalcode.example.config; + +import com.eternalcode.multification.notice.Notice; +import net.dzikoysk.cdn.entity.Description; +import net.kyori.adventure.bossbar.BossBar; + +import java.time.Duration; + +public class MessagesConfig { + + @Description("# Join message") + public Notice joinedTheServer = Notice.builder() + .chat(" has joined the server!") + .bossBar( + BossBar.Color.GREEN, + BossBar.Overlay.PROGRESS, + Duration.ofSeconds(5), + 1.0F, + " has joined the server!" + ) + .sound("minecraft:entity.player.levelup", 1.0F, 1.0F) + .build(); + + @Description("# Server switch message") + public Notice switchedServer = Notice.builder() + .chat("Switched to !") + .sound("minecraft:entity.enderman.teleport", 1.0F, 1.0F) + .build(); + + public Notice reloadMessage = Notice.builder() + .chat("Configuration has been reloaded!") + .sound("minecraft:ambient.basalt_deltas.additions", 1.0F, 1.0F) + .build(); +} diff --git a/examples/velocity/src/main/java/com/eternalcode/example/notice/ExampleMultification.java b/examples/velocity/src/main/java/com/eternalcode/example/notice/ExampleMultification.java new file mode 100644 index 0000000..3c0c2d6 --- /dev/null +++ b/examples/velocity/src/main/java/com/eternalcode/example/notice/ExampleMultification.java @@ -0,0 +1,40 @@ +package com.eternalcode.example.notice; + + +import com.eternalcode.example.config.MessagesConfig; +import com.eternalcode.multification.adventure.AudienceConverter; +import com.eternalcode.multification.translation.TranslationProvider; +import com.eternalcode.multification.velocity.VelocityMultification; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.ProxyServer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.jetbrains.annotations.NotNull; + +public class ExampleMultification extends VelocityMultification { + + private final MessagesConfig messagesConfig; + private final MiniMessage miniMessage; + + public ExampleMultification(Object plugin, ProxyServer server, MessagesConfig messagesConfig) { + super(server, plugin); + this.messagesConfig = messagesConfig; + this.miniMessage = MiniMessage.miniMessage(); + } + + @Override + protected @NotNull TranslationProvider translationProvider() { + return locale -> this.messagesConfig; + } + + @Override + protected @NotNull ComponentSerializer serializer() { + return this.miniMessage; + } + + @Override + protected @NotNull AudienceConverter audienceConverter() { + return commandSender -> commandSender; + } +} diff --git a/multification-velocity/build.gradle.kts b/multification-velocity/build.gradle.kts new file mode 100644 index 0000000..6207a03 --- /dev/null +++ b/multification-velocity/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + `multification-java` + `multification-java-17` + `multification-repositories` + `multification-publish` +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +dependencies { + api(project(":multification-core")) + compileOnly("com.velocitypowered:velocity-api:${Versions.VELOCITY_API}") + testImplementation("com.velocitypowered:velocity-api:${Versions.VELOCITY_API}") +} + diff --git a/multification-velocity/src/com/eternalcode/multification/velocity/VelocityLocaleProvider.java b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityLocaleProvider.java new file mode 100644 index 0000000..a37af3a --- /dev/null +++ b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityLocaleProvider.java @@ -0,0 +1,19 @@ +package com.eternalcode.multification.velocity; + +import com.eternalcode.multification.locate.LocaleProvider; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public class VelocityLocaleProvider implements LocaleProvider { + + @Override + public @NotNull Locale provide(CommandSource commandSource) { + if (commandSource instanceof Player player) { + return player.getEffectiveLocale() != null ? player.getEffectiveLocale() : Locale.ROOT; + } + return Locale.ROOT; + } +} diff --git a/multification-velocity/src/com/eternalcode/multification/velocity/VelocityMultification.java b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityMultification.java new file mode 100644 index 0000000..2ce14fd --- /dev/null +++ b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityMultification.java @@ -0,0 +1,42 @@ +package com.eternalcode.multification.velocity; + +import com.eternalcode.multification.Multification; +import com.eternalcode.multification.executor.AsyncExecutor; +import com.eternalcode.multification.locate.LocaleProvider; +import com.eternalcode.multification.viewer.ViewerProvider; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.ProxyServer; +import org.jetbrains.annotations.NotNull; + +public abstract class VelocityMultification extends Multification { + + public static final LocaleProvider DEFAULT_LOCALE_PROVIDER = new VelocityLocaleProvider(); + + private final ProxyServer server; + private final Object plugin; + private final ViewerProvider defaultViewerProvider; + + protected VelocityMultification(ProxyServer server, Object plugin) { + super(); + this.server = server; + this.plugin = plugin; + this.defaultViewerProvider = new VelocityViewerProvider(server); + } + + @Override + protected @NotNull ViewerProvider viewerProvider() { + return defaultViewerProvider; + } + + @Override + protected @NotNull LocaleProvider localeProvider() { + return DEFAULT_LOCALE_PROVIDER; + } + + @Override + protected @NotNull AsyncExecutor asyncExecutor() { + return (runnable) -> server.getScheduler() + .buildTask(this.plugin, runnable); + } +} + diff --git a/multification-velocity/src/com/eternalcode/multification/velocity/VelocityViewerProvider.java b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityViewerProvider.java new file mode 100644 index 0000000..7a28ad5 --- /dev/null +++ b/multification-velocity/src/com/eternalcode/multification/velocity/VelocityViewerProvider.java @@ -0,0 +1,55 @@ +package com.eternalcode.multification.velocity; + +import com.eternalcode.multification.viewer.ViewerProvider; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; + +import java.util.Collection; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +public class VelocityViewerProvider implements ViewerProvider { + + private final ProxyServer server; + + public VelocityViewerProvider(ProxyServer server) { + this.server = server; + } + + @Override + public CommandSource console() { + return this.server.getConsoleCommandSource(); + } + + @Override + public CommandSource player(UUID uuid) { + Optional player = this.server.getPlayer(uuid); + return player.orElse(null); + } + + @Override + public Collection onlinePlayers() { + return server.getAllPlayers() + .stream() + .map(player -> (CommandSource) player) + .collect(Collectors.toList()); + } + + @Override + public Collection onlinePlayers(String permission) { + return server.getAllPlayers() + .stream() + .filter(player -> player.hasPermission(permission)) + .map(player -> (CommandSource) player) + .collect(Collectors.toList()); + } + + @Override + public Collection all() { + Collection viewers = this.onlinePlayers(); + viewers.add(this.console()); + return viewers; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 487047b..a6b22bc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,5 +5,8 @@ include("multification-cdn") include("multification-okaeri") include("multification-bukkit") include("multification-paper") +include("multification-velocity") + include("examples:bukkit") include("examples:paper") +include("examples:velocity")