From f386740e2ad8ecd70f5d0ee77b8376e5a6db6f1a Mon Sep 17 00:00:00 2001 From: Disy Date: Mon, 5 Oct 2026 11:15:02 +0800 Subject: [PATCH] Add Broadcast plugin source --- .gitignore | 119 ++++++++++++++++++ build.gradle.kts | 54 ++++++++ gradle.properties | 0 gradle/wrapper/gradle-wrapper.properties | 1 + settings.gradle.kts | 1 + src/main/kotlin/cn/disy920/broadcast/Main.kt | 59 +++++++++ .../cn/disy920/broadcast/config/Config.kt | 33 +++++ .../disy920/broadcast/config/ConfigLoader.kt | 41 ++++++ .../cn/disy920/broadcast/BuildConstants.java | 7 ++ 9 files changed, 315 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle.kts create mode 100644 src/main/kotlin/cn/disy920/broadcast/Main.kt create mode 100644 src/main/kotlin/cn/disy920/broadcast/config/Config.kt create mode 100644 src/main/kotlin/cn/disy920/broadcast/config/ConfigLoader.kt create mode 100644 src/main/templates/cn/disy920/broadcast/BuildConstants.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5f737e --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..53ede6f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,54 @@ +import org.jetbrains.gradle.ext.settings +import org.jetbrains.gradle.ext.taskTriggers + +plugins { + kotlin("jvm") version "2.0.20-Beta1" + // kotlin("kapt") version "2.0.20-Beta1" + kotlin("plugin.serialization") version "2.0.20-Beta1" + + id("com.github.johnrengelman.shadow") version "8.1.1" + id("eclipse") + id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.8" +} + +group = "cn.disy920" +version = "1.0.0" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") { + name = "papermc-repo" + } + maven("https://oss.sonatype.org/content/groups/public/") { + name = "sonatype" + } +} + +dependencies { + compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") + compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") + + compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0") +} + +val targetJavaVersion = 17 +kotlin { + jvmToolchain(targetJavaVersion) +} + +val templateSource = file("src/main/templates") +val templateDest = layout.buildDirectory.dir("generated/sources/templates") +val generateTemplates = tasks.register("generateTemplates") { + val props = mapOf("version" to project.version) + inputs.properties(props) + + from(templateSource) + into(templateDest) + expand(props) +} + +sourceSets.main.configure { java.srcDir(generateTemplates.map { it.outputs }) } + +project.idea.project.settings.taskTriggers.afterSync(generateTemplates) +project.eclipse.synchronizationTasks(generateTemplates) diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0d8ab51 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..832196a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "Broadcast" diff --git a/src/main/kotlin/cn/disy920/broadcast/Main.kt b/src/main/kotlin/cn/disy920/broadcast/Main.kt new file mode 100644 index 0000000..ca79eb7 --- /dev/null +++ b/src/main/kotlin/cn/disy920/broadcast/Main.kt @@ -0,0 +1,59 @@ +package cn.disy920.broadcast; + +import cn.disy920.broadcast.config.ConfigLoader +import com.google.inject.Inject +import com.velocitypowered.api.event.Subscribe +import com.velocitypowered.api.event.player.PlayerChatEvent +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent +import com.velocitypowered.api.plugin.Plugin +import com.velocitypowered.api.plugin.annotation.DataDirectory +import com.velocitypowered.api.proxy.ProxyServer +import net.kyori.adventure.text.Component +import org.slf4j.Logger +import java.nio.file.Path +import kotlin.jvm.optionals.getOrNull + +@Plugin( + id = "broadcast", + name = "Broadcast", + version = BuildConstants.VERSION, + url = "www.starlight.cool", + authors = ["Disy920"] +) +class Main @Inject constructor( + private val proxyServer: ProxyServer, + private val logger: Logger, + @DataDirectory private val dataDirectory: Path +) { + + private val configLoader = ConfigLoader(dataDirectory) + + private val config + get() = configLoader.load() + + @Subscribe + fun onProxyInitialization(event: ProxyInitializeEvent) { + logger.info("跨服聊天插件 v${BuildConstants.VERSION} 已加载,作者Disy920") + } + + @Subscribe + fun onPlayerChat(event: PlayerChatEvent) { + if (!config.enable) return + + val player = event.player + val message = event.message + + val serverName = player.currentServer?.getOrNull()?.serverInfo?.name ?: return + if (config.bannedServer.contains(serverName)) return + + val displayServerName = config.prefixMap.getOrDefault(serverName, serverName) + + logger.info("[$displayServerName] <${player.username}> $message") + + for (subServer in proxyServer.allServers) { + if (subServer?.serverInfo?.name == serverName) continue + + subServer.sendMessage(Component.text("[$displayServerName] <${player.username}> $message").color(config.broadcastColor)) + } + } +} diff --git a/src/main/kotlin/cn/disy920/broadcast/config/Config.kt b/src/main/kotlin/cn/disy920/broadcast/config/Config.kt new file mode 100644 index 0000000..e2d5ddf --- /dev/null +++ b/src/main/kotlin/cn/disy920/broadcast/config/Config.kt @@ -0,0 +1,33 @@ +package cn.disy920.broadcast.config + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextColor + +@Serializable +@JvmRecord +data class Config( + val enable: Boolean = false, + val prefixMap: Map = emptyMap(), // 名字的映射,可以把默认的服务器名转化成一个自定义名称 + val bannedServer: Set = emptySet(), // 不接收跨服聊天的服务器 + @Serializable(with = ColorSerializer::class) + val broadcastColor: TextColor = NamedTextColor.GRAY, // 跨服聊天消息的颜色 +) + +private object ColorSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("TextColor", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): TextColor { + return TextColor.color(decoder.decodeInt()) + } + + override fun serialize(encoder: Encoder, value: TextColor) { + encoder.encodeInt(value.value()) + } +} diff --git a/src/main/kotlin/cn/disy920/broadcast/config/ConfigLoader.kt b/src/main/kotlin/cn/disy920/broadcast/config/ConfigLoader.kt new file mode 100644 index 0000000..f2382f8 --- /dev/null +++ b/src/main/kotlin/cn/disy920/broadcast/config/ConfigLoader.kt @@ -0,0 +1,41 @@ +package cn.disy920.broadcast.config + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.nio.file.Path + +class ConfigLoader(rootDir: Path) { + private val configFile = rootDir.resolve("config.json").toFile() + + private val json = Json { + prettyPrint = true + ignoreUnknownKeys = true + encodeDefaults = true + } + + private lateinit var _config: Config + + val config: Config + get() = load() + + fun load(): Config { + if (!::_config.isInitialized) reload() + return _config + } + + fun save(config: Config) { + configFile.writeText(json.encodeToString(config)) + } + + fun reload() { + if (!configFile.isFile) { + configFile.createNewFile() + + _config = Config() + configFile.writeText(json.encodeToString(_config)) + } + else { + _config = json.decodeFromString(configFile.readText()) + } + } +} \ No newline at end of file diff --git a/src/main/templates/cn/disy920/broadcast/BuildConstants.java b/src/main/templates/cn/disy920/broadcast/BuildConstants.java new file mode 100644 index 0000000..6d1eba7 --- /dev/null +++ b/src/main/templates/cn/disy920/broadcast/BuildConstants.java @@ -0,0 +1,7 @@ +package cn.disy920.broadcast; + +// The constants are replaced before compilation +public class BuildConstants { + + public static final String VERSION = "${version}"; +}