diff --git a/README.md b/README.md index 23ee2143..79159b84 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ - Template System - Custom Placeholders System - Per-permission miniMessages Tags (see permissions below) +- Mentions System - mention players with @playername and they will hear a sound ## 🔗 Useful links: @@ -151,6 +152,18 @@ placeholders: # It is used to shorten the text even more and keep the clean file! templates: - "$hoverName($name) -> 'Name: $name

{rankDescription}
{joinDate}
{health}
{lvl}

{privateMessage}'>{displayname}'" + +# Mention system settings +mentions: + # When a player mentions another player with @playername, the mentioned player will hear a sound + enabled: true + # The sound to play when a player is mentioned + # Available sounds: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Sound.html + sound: "BLOCK_NOTE_BLOCK_PLING" + # The volume of the mention sound (0.0 to 1.0) + volume: 1.0 + # The pitch of the mention sound (0.5 to 2.0) + pitch: 1.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 7909327d..3544e144 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import java.io.FileOutputStream import java.io.IOException +import java.io.Serializable import java.util.jar.JarEntry import java.util.jar.JarFile import java.util.jar.JarOutputStream @@ -23,91 +24,93 @@ tasks.register("shadowChatFormatter") { this.dependsOn("${targetProject.name}:shadowJar") } - this.doLast { - this@register.mergeJars( - "ChatFormatter v${project.version}.jar", - targetProjects - ) - } -} + val projectVersion = project.version.toString() + val outputFile = project.layout.buildDirectory.file("libs/ChatFormatter v${projectVersion}.jar") + .map { it.asFile } -fun ShadowJar.mergeJars(archiveFileName: String, projects: List) { - val outputFile = File( - this.project.layout.buildDirectory.asFile.get(), - "libs/$archiveFileName" - ) - val outputDir = outputFile.parentFile - ?: throw IllegalStateException("Cannot find output directory") + val shadowJarFiles = targetProjects + .map { targetProject -> targetProject.tasks.named("shadowJar", ShadowJar::class.java).get().archiveFile.get().asFile } - if (!outputDir.exists() && !outputDir.mkdirs()) { - throw IllegalStateException("Failed to create directory: ${outputDir.absolutePath}") - } + val merger = JarMerger(outputFile.get(), shadowJarFiles) - if (outputFile.exists() && !outputFile.delete()) { - throw IllegalStateException("Cannot delete existing file: ${outputFile.absolutePath}") - } + inputs.files(shadowJarFiles) + outputs.files(outputFile) - if (!outputFile.createNewFile()) { - throw IllegalStateException("Cannot create output file: ${outputFile.absolutePath}") + doLast { + merger.mergeJars() } - - mergeShadowJarsIntoOutput(outputFile, projects) } -private fun mergeShadowJarsIntoOutput( - outputFile: File, - projects: List -) { - JarOutputStream(FileOutputStream(outputFile)).use { outputJar -> - val processedEntries = mutableSetOf() +class JarMerger( + private val outputFile: File, + private val inputFiles: List +) : Serializable { + + fun mergeJars() { + val outputDir = outputFile.parentFile + ?: throw IllegalStateException("Cannot find output directory") + + if (!outputDir.exists() && !outputDir.mkdirs()) { + throw IllegalStateException("Failed to create directory: ${outputDir.absolutePath}") + } + + if (outputFile.exists() && !outputFile.delete()) { + throw IllegalStateException("Cannot delete existing file: ${outputFile.absolutePath}") + } + + if (!outputFile.createNewFile()) { + throw IllegalStateException("Cannot create output file: ${outputFile.absolutePath}") + } - for (targetProject in projects) { - val shadowJarTask = targetProject.tasks.named("shadowJar", ShadowJar::class.java).get() + JarOutputStream(FileOutputStream(outputFile)).use { outputJar -> + val processedEntries = mutableSetOf() - for (jarFile in shadowJarTask.outputs.files.files) { + for (jarFile in inputFiles) { processJarFile(jarFile, outputJar, processedEntries) } } } -} -private fun processJarFile( - jarFile: File, - outputJar: JarOutputStream, - processedEntries: MutableSet -) { - JarFile(jarFile).use { sourceJar -> - for (entry in sourceJar.entries()) { - if (entry.isDirectory || processedEntries.contains(entry.name)) { - continue - } - try { - copyJarEntry(sourceJar, entry, outputJar) - processedEntries.add(entry.name) - } catch (exception: IOException) { - if (exception.message?.contains("duplicate entry:") != true) { - throw exception + private fun processJarFile( + jarFile: File, + outputJar: JarOutputStream, + processedEntries: MutableSet + ) { + JarFile(jarFile).use { sourceJar -> + for (entry in sourceJar.entries()) { + if (entry.isDirectory || processedEntries.contains(entry.name)) { + continue + } + + try { + copyJarEntry(sourceJar, entry, outputJar) + processedEntries.add(entry.name) + } catch (exception: IOException) { + if (exception.message?.contains("duplicate entry:") != true) { + throw exception + } } } } } -} -private fun copyJarEntry( - sourceJar: JarFile, - entry: JarEntry, - outputJar: JarOutputStream -) { - val entryBytes = sourceJar.getInputStream(entry).use { it.readBytes() } - val newEntry = JarEntry(entry.name).apply { - this.time = System.currentTimeMillis() - this.size = entryBytes.size.toLong() + private fun copyJarEntry( + sourceJar: JarFile, + entry: JarEntry, + outputJar: JarOutputStream + ) { + val entryBytes = sourceJar.getInputStream(entry).use { it.readBytes() } + val newEntry = JarEntry(entry.name).apply { + this.time = System.currentTimeMillis() + this.size = entryBytes.size.toLong() + } + + outputJar.putNextEntry(newEntry) + outputJar.write(entryBytes) + outputJar.closeEntry() } - outputJar.putNextEntry(newEntry) - outputJar.write(entryBytes) - outputJar.closeEntry() } runPaper { @@ -115,18 +118,20 @@ runPaper { } tasks.runServer { - minecraftVersion("1.21.10") + minecraftVersion("1.21.11") dependsOn("shadowChatFormatter") pluginJars.from(layout.buildDirectory.file("libs/ChatFormatter v${project.version}.jar")) + jvmArgs("-Dcom.mojang.eula.agree=true", "-XX:+AllowEnhancedClassRedefinition") javaLauncher.set( javaToolchains.launcherFor { + this.vendor = JvmVendorSpec.JETBRAINS this.languageVersion.set(JavaLanguageVersion.of(21)) } ) downloadPlugins { - this.modrinth("luckperms", "v5.5.0-bukkit") - this.modrinth("VaultUnlocked", "2.16.0") + this.modrinth("luckperms", "v5.5.17-bukkit") + this.modrinth("VaultUnlocked", "2.19.0") } } \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index dbac5280..5d85bcbc 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,5 +10,5 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.10") implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.3.1") - implementation("net.minecrell:plugin-yml:0.6.0") + implementation("de.eldoria.plugin-yml.bukkit:de.eldoria.plugin-yml.bukkit.gradle.plugin:0.8.0") } diff --git a/buildSrc/src/main/kotlin/eternalcode.java.gradle.kts b/buildSrc/src/main/kotlin/eternalcode.java.gradle.kts index 927d8615..31feb09f 100644 --- a/buildSrc/src/main/kotlin/eternalcode.java.gradle.kts +++ b/buildSrc/src/main/kotlin/eternalcode.java.gradle.kts @@ -13,7 +13,7 @@ repositories { maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") } maven { url = uri("https://jitpack.io") } maven { url = uri("https://repo.eternalcode.pl/releases") } - maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") } } java { diff --git a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterApi.java b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterApi.java index 806ff2ae..d160de99 100644 --- a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterApi.java +++ b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterApi.java @@ -1,5 +1,6 @@ package com.eternalcode.formatter; +import com.eternalcode.formatter.mention.MentionService; import com.eternalcode.formatter.rank.ChatRankProvider; import com.eternalcode.formatter.template.TemplateService; import com.eternalcode.formatter.placeholder.PlaceholderRegistry; @@ -14,4 +15,6 @@ public interface ChatFormatterApi { ChatHandler getChatHandler(); + MentionService getMentionService(); + } diff --git a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterPlugin.java b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterPlugin.java index 3d4ff69a..b1280289 100644 --- a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterPlugin.java +++ b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatFormatterPlugin.java @@ -2,9 +2,12 @@ import com.eternalcode.formatter.config.ConfigManager; import com.eternalcode.formatter.config.PluginConfig; +import com.eternalcode.formatter.mention.MentionConfig; +import com.eternalcode.formatter.mention.MentionListener; +import com.eternalcode.formatter.mention.MentionService; import com.eternalcode.formatter.placeholder.ConfiguredReplacer; -import com.eternalcode.formatter.placeholderapi.PlaceholderAPIInitializer; import com.eternalcode.formatter.placeholder.PlaceholderRegistry; +import com.eternalcode.formatter.placeholderapi.PlaceholderAPIInitializer; import com.eternalcode.formatter.rank.ChatRankProvider; import com.eternalcode.formatter.rank.VaultInitializer; import com.eternalcode.formatter.template.TemplateService; @@ -26,6 +29,7 @@ public class ChatFormatterPlugin implements ChatFormatterApi { private final TemplateService templateService; private final ChatRankProvider rankProvider; private final ChatHandler chatHandler; + private final MentionService mentionService; public ChatFormatterPlugin(Plugin plugin) { Server server = plugin.getServer(); @@ -36,6 +40,7 @@ public ChatFormatterPlugin(Plugin plugin) { PluginConfig pluginConfig = configManager.getPluginConfig(); + // PlaceholderAPI support this.placeholderRegistry = new PlaceholderRegistry(); PlaceholderAPIInitializer.initialize(server, this.placeholderRegistry); this.placeholderRegistry.addReplacer(new ConfiguredReplacer(pluginConfig)); @@ -52,6 +57,12 @@ public ChatFormatterPlugin(Plugin plugin) { this.chatHandler = new ChatHandlerImpl(miniMessage, pluginConfig, this.rankProvider, this.placeholderRegistry, this.templateService); server.getPluginCommand("chatformatter").setExecutor(new ChatFormatterCommand(configManager, audienceProvider, miniMessage)); + + // Mentions + this.mentionService = new MentionService(server, pluginConfig); + server.getPluginManager().registerEvents(new MentionListener(mentionService), plugin); + + // Update checker server.getPluginManager().registerEvents(new UpdaterController(updaterService, pluginConfig, audienceProvider, miniMessage), plugin); ChatFormatterApiProvider.enable(this); @@ -83,4 +94,9 @@ public ChatHandler getChatHandler() { return this.chatHandler; } + @Override + public MentionService getMentionService() { + return this.mentionService; + } + } diff --git a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatHandlerImpl.java b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatHandlerImpl.java index 0b82fb04..d0980db2 100644 --- a/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatHandlerImpl.java +++ b/chatformatter-core/src/main/java/com/eternalcode/formatter/ChatHandlerImpl.java @@ -1,15 +1,11 @@ package com.eternalcode.formatter; import com.eternalcode.formatter.adventure.AdventureUrlPostProcessor; -import java.util.Optional; -import net.kyori.adventure.text.serializer.json.JSONOptions; -import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; - import com.eternalcode.formatter.adventure.TextColorTagResolver; import com.eternalcode.formatter.legacy.Legacy; +import com.eternalcode.formatter.placeholder.PlaceholderRegistry; import com.eternalcode.formatter.rank.ChatRankProvider; import com.eternalcode.formatter.template.TemplateService; -import com.eternalcode.formatter.placeholder.PlaceholderRegistry; import com.google.common.collect.ImmutableMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -19,11 +15,15 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.standard.StandardTags; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.json.JSONOptions; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; + +import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; class ChatHandlerImpl implements ChatHandler { @@ -75,7 +75,7 @@ class ChatHandlerImpl implements ChatHandler { .build(); private static final MiniMessage EMPTY_MESSAGE_DESERIALIZER = MiniMessage.builder() - .postProcessor(new AdventureUrlPostProcessor()) + .postProcessor(new AdventureUrlPostProcessor()) .tags(TagResolver.empty()) .build(); diff --git a/chatformatter-core/src/main/java/com/eternalcode/formatter/config/PluginConfig.java b/chatformatter-core/src/main/java/com/eternalcode/formatter/config/PluginConfig.java index 90ca6989..2243fa79 100644 --- a/chatformatter-core/src/main/java/com/eternalcode/formatter/config/PluginConfig.java +++ b/chatformatter-core/src/main/java/com/eternalcode/formatter/config/PluginConfig.java @@ -1,5 +1,6 @@ package com.eternalcode.formatter.config; +import com.eternalcode.formatter.mention.MentionConfig; import com.eternalcode.formatter.template.Template; import com.eternalcode.formatter.template.TemplateRepository; import com.google.common.collect.ImmutableList; @@ -83,6 +84,9 @@ public class PluginConfig implements ChatSettings, TemplateRepository { .add(Template.of("hoverName", List.of("name"), "Name: $name

{rankDescription}
{joinDate}
{health}
{lvl}

{privateMessage}'>{displayname}")) .build(); + @Description({ " ", "# Mention system settings" }) + public MentionConfig mentions = new MentionConfig(); + @Override public boolean isReceiveUpdates() { @@ -98,4 +102,5 @@ public String getRawFormat(String rank) { public List