diff --git a/build.gradle.kts b/build.gradle.kts index 593a247..d36b426 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,11 +6,12 @@ plugins { id("fabric-loom") } -group = "net.rk4z.fabricord" -version = "4.0.2" +group = "net.ririfa.fabricord" +version = "4.1.0-alpha" repositories { mavenCentral() + maven("https://repo.ririfa.net/maven2/") } val includeInJar: Configuration by configurations.creating @@ -29,13 +30,14 @@ dependencies { modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricVersion") modImplementation("net.fabricmc:fabric-language-kotlin:$fabricLanguageKotlinVersion") - implementation("net.dv8tion:JDA:5.2.1") { + modApi("net.dv8tion:JDA:5.2.1") { exclude("net.java.dev.jna", "jna") } - implementation("org.yaml:snakeyaml:2.3") - implementation("net.kyori:adventure-text-serializer-gson:4.17.0") - implementation("com.github.ben-manes.caffeine:caffeine:3.1.8") + modApi("org.yaml:snakeyaml:2.3") + modApi("net.kyori:adventure-text-serializer-gson:4.17.0") + modApi("com.github.ben-manes.caffeine:caffeine:3.1.8") + modApi("net.ririfa:langman:1.1.0") // TODO: Is loom provide this? includeInJar("net.dv8tion:JDA:5.2.1") { diff --git a/gradle.properties b/gradle.properties index 4e5b561..f45deee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx4096M # Minecraft minecraftVersion=1.21.4 -mappingsVersion=1.21.4+build.1 +mappingsVersion=1.21.4+build.8 fabricVersion=0.107.0+1.21.3 # Global Setting diff --git a/src/main/java/net/rk4z/fabricord/mixin/PlayerAdvancementTrackerMixin.java b/src/main/java/net/ririfa/fabricord/mixin/PlayerAdvancementTrackerMixin.java similarity index 94% rename from src/main/java/net/rk4z/fabricord/mixin/PlayerAdvancementTrackerMixin.java rename to src/main/java/net/ririfa/fabricord/mixin/PlayerAdvancementTrackerMixin.java index e9578d7..575dc74 100644 --- a/src/main/java/net/rk4z/fabricord/mixin/PlayerAdvancementTrackerMixin.java +++ b/src/main/java/net/ririfa/fabricord/mixin/PlayerAdvancementTrackerMixin.java @@ -1,8 +1,8 @@ -package net.rk4z.fabricord.mixin; +package net.ririfa.fabricord.mixin; import net.minecraft.advancement.*; import net.minecraft.server.network.ServerPlayerEntity; -import net.rk4z.fabricord.discord.DiscordEmbed; +import net.ririfa.fabricord.discord.DiscordEmbed; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/java/net/rk4z/fabricord/mixin/ServerPlayerEntityMixin.java b/src/main/java/net/ririfa/fabricord/mixin/ServerPlayerEntityMixin.java similarity index 90% rename from src/main/java/net/rk4z/fabricord/mixin/ServerPlayerEntityMixin.java rename to src/main/java/net/ririfa/fabricord/mixin/ServerPlayerEntityMixin.java index 46082a0..a98a0be 100644 --- a/src/main/java/net/rk4z/fabricord/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/net/ririfa/fabricord/mixin/ServerPlayerEntityMixin.java @@ -1,9 +1,9 @@ -package net.rk4z.fabricord.mixin; +package net.ririfa.fabricord.mixin; import net.minecraft.entity.damage.DamageSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; -import net.rk4z.fabricord.discord.DiscordEmbed; +import net.ririfa.fabricord.discord.DiscordEmbed; import org.jetbrains.annotations.Contract; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt b/src/main/kotlin/net/ririfa/fabricord/Fabricord.kt similarity index 71% rename from src/main/kotlin/net/rk4z/fabricord/Fabricord.kt rename to src/main/kotlin/net/ririfa/fabricord/Fabricord.kt index 06de35e..ada52e3 100644 --- a/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt +++ b/src/main/kotlin/net/ririfa/fabricord/Fabricord.kt @@ -1,4 +1,4 @@ -package net.rk4z.fabricord +package net.ririfa.fabricord import net.fabricmc.api.DedicatedServerModInitializer import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents @@ -6,29 +6,29 @@ import net.fabricmc.fabric.api.message.v1.ServerMessageEvents import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents import net.fabricmc.loader.api.FabricLoader import net.minecraft.text.Text -import net.rk4z.fabricord.discord.DiscordBotManager -import net.rk4z.fabricord.discord.DiscordEmbed -import net.rk4z.fabricord.discord.DiscordPlayerEventHandler.handleMCMessage -import net.rk4z.fabricord.utils.Utils.copyResourceToFile -import net.rk4z.fabricord.utils.Utils.getNullableBoolean -import net.rk4z.fabricord.utils.Utils.getNullableString +import net.ririfa.langman.LangMan +import net.ririfa.fabricord.discord.DiscordBotManager +import net.ririfa.fabricord.discord.DiscordEmbed +import net.ririfa.fabricord.discord.DiscordPlayerEventHandler.handleMCMessage +import net.ririfa.fabricord.translation.FabricordMessageKey +import net.ririfa.fabricord.utils.Utils.copyResourceToFile +import net.ririfa.fabricord.utils.Utils.getNullableBoolean +import net.ririfa.fabricord.utils.Utils.getNullableString +import net.ririfa.langman.InitType import org.slf4j.Logger import org.slf4j.LoggerFactory import org.yaml.snakeyaml.Yaml +import java.io.File import java.io.IOException -import java.io.OutputStream -import java.io.PrintStream import java.nio.file.Files import java.nio.file.Path import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService -import java.util.concurrent.TimeUnit import kotlin.io.path.notExists class Fabricord : DedicatedServerModInitializer { companion object { private const val MOD_ID = "fabricord" - val logger: Logger = LoggerFactory.getLogger(Fabricord::class.simpleName) private val loader: FabricLoader = FabricLoader.getInstance() @@ -37,6 +37,8 @@ class Fabricord : DedicatedServerModInitializer { private val serverDir: Path = loader.gameDir.toRealPath() private val modDir: Path = serverDir.resolve(MOD_ID) private val configFile: Path = modDir.resolve("config.yml") + private val langDir: File = serverDir.resolve("lang").toFile() + private val availableLang = listOf("en", "ja") private val yaml = Yaml() private var initializeIsDone = false @@ -66,23 +68,29 @@ class Fabricord : DedicatedServerModInitializer { } override fun onInitializeServer() { - logger.info("Initializing Fabricord...") + val lm = LangMan.createNew( + { Text.of(it) }, + FabricordMessageKey::class + ) + + lm.init(InitType.YAML, langDir, availableLang) + + logger.info(lm.getSysMessage(FabricordMessageKey.System.Initializing)) checkRequiredFilesAndDirectories() loadConfig() if (requiredNullCheck()) { - logger.error("Bot token or log channel ID is missing in config file.") - logger.error("Maybe you are running the mod for the first time?") - logger.error("Please check the config file at $configFile") + logger.error(lm.getSysMessage(FabricordMessageKey.System.MissingRequiredProp.ITEM1)) + logger.error(lm.getSysMessage(FabricordMessageKey.System.MissingRequiredProp.ITEM2)) + logger.error(lm.getSysMessage(FabricordMessageKey.System.MissingRequiredProp.ITEM3, configFile)) return } nullCheck() registerEvents() - ConsoleListener.register() initializeIsDone = true - logger.info("Fabricord initialized successfully.") + logger.info(lm.getSysMessage(FabricordMessageKey.System.Initialized)) } private fun registerEvents() { @@ -135,83 +143,6 @@ class Fabricord : DedicatedServerModInitializer { } } - object ConsoleListener { - private val buffer = StringBuilder() - private var lastFlushTime = System.currentTimeMillis() - private var lastMessageType: String? = null - - private val executorService = Executors.newSingleThreadScheduledExecutor() - - fun register() { - val customOut = createInterceptedStream("STDOUT") - val customErr = createInterceptedStream("STDERR") - - System.setOut(customOut) - System.setErr(customErr) - - executorService.scheduleAtFixedRate(::flushLogsIfNeeded, 100, 100, TimeUnit.MILLISECONDS) - } - - private fun createInterceptedStream(type: String): PrintStream { - return PrintStream(object : OutputStream() { - private val localBuffer = StringBuilder() - - override fun write(b: Int) { - val ch = b.toChar() - localBuffer.append(ch) - - if (ch == '\n') { - val message = localBuffer.toString().trim() - localBuffer.setLength(0) - - if (message.isNotBlank()) { - processConsoleMessage(message, type) - } - } - } - }, true) - } - - private fun processConsoleMessage(message: String, type: String) { - synchronized(buffer) { - if (lastMessageType != null && lastMessageType != type) { - // STDOUTとSTDERRが切り替わったら、バッファをフラッシュ - flushLogs() - } - - buffer.append(message).append("\n") - lastMessageType = type - lastFlushTime = System.currentTimeMillis() - } - } - - private fun flushLogsIfNeeded() { - synchronized(buffer) { - if (buffer.isNotEmpty() && System.currentTimeMillis() - lastFlushTime > 300) { - flushLogs() - } - } - } - - private fun flushLogs() { - synchronized(buffer) { - if (buffer.isNotEmpty()) { - val message = buffer.toString().trim() - buffer.setLength(0) - - if (lastMessageType == "STDERR") { - DiscordBotManager.sendToDiscordConsole("```ansi\n$message\n```") // 赤色 - } else { - DiscordBotManager.sendToDiscordConsole("```$message```") - } - - lastMessageType = null - } - } - } - } - - //>------------------------------------------------------------------------------------------------------------------<\\ private fun checkRequiredFilesAndDirectories() { diff --git a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordBotManager.kt similarity index 98% rename from src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt rename to src/main/kotlin/net/ririfa/fabricord/discord/DiscordBotManager.kt index 7898ddb..e2b81cb 100644 --- a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt +++ b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordBotManager.kt @@ -1,4 +1,4 @@ -package net.rk4z.fabricord.discord +package net.ririfa.fabricord.discord import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.JDA @@ -13,8 +13,8 @@ import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.requests.GatewayIntent import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity -import net.rk4z.fabricord.Fabricord -import net.rk4z.fabricord.Fabricord.Companion.executorService +import net.ririfa.fabricord.Fabricord +import net.ririfa.fabricord.Fabricord.Companion.executorService import java.awt.Color import java.util.* import java.util.concurrent.TimeUnit diff --git a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordEmbed.kt b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordEmbed.kt similarity index 90% rename from src/main/kotlin/net/rk4z/fabricord/discord/DiscordEmbed.kt rename to src/main/kotlin/net/ririfa/fabricord/discord/DiscordEmbed.kt index 1cf0d4f..156c921 100644 --- a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordEmbed.kt +++ b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordEmbed.kt @@ -1,11 +1,11 @@ -package net.rk4z.fabricord.discord +package net.ririfa.fabricord.discord -import net.rk4z.fabricord.Fabricord +import net.ririfa.fabricord.Fabricord import net.dv8tion.jda.api.EmbedBuilder import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text -import net.rk4z.fabricord.Fabricord.Companion.playerJoinMessage -import net.rk4z.fabricord.Fabricord.Companion.playerLeaveMessage +import net.ririfa.fabricord.Fabricord.Companion.playerJoinMessage +import net.ririfa.fabricord.Fabricord.Companion.playerLeaveMessage import java.awt.Color object DiscordEmbed { diff --git a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordMessageHandler.kt b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordMessageHandler.kt similarity index 97% rename from src/main/kotlin/net/rk4z/fabricord/discord/DiscordMessageHandler.kt rename to src/main/kotlin/net/ririfa/fabricord/discord/DiscordMessageHandler.kt index 53c52da..8753fe5 100644 --- a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordMessageHandler.kt +++ b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordMessageHandler.kt @@ -1,4 +1,4 @@ -package net.rk4z.fabricord.discord +package net.ririfa.fabricord.discord import net.dv8tion.jda.api.events.message.MessageReceivedEvent import net.kyori.adventure.text.Component @@ -12,8 +12,8 @@ import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvents import net.minecraft.text.Text -import net.rk4z.fabricord.Fabricord -import net.rk4z.fabricord.utils.Utils.replaceUUIDsWithMCIDs +import net.ririfa.fabricord.Fabricord +import net.ririfa.fabricord.utils.Utils.replaceUUIDsWithMCIDs import java.awt.Color object DiscordMessageHandler { diff --git a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordPlayerEventHandler.kt b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordPlayerEventHandler.kt similarity index 95% rename from src/main/kotlin/net/rk4z/fabricord/discord/DiscordPlayerEventHandler.kt rename to src/main/kotlin/net/ririfa/fabricord/discord/DiscordPlayerEventHandler.kt index 6f8a6fc..b44c39f 100644 --- a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordPlayerEventHandler.kt +++ b/src/main/kotlin/net/ririfa/fabricord/discord/DiscordPlayerEventHandler.kt @@ -1,8 +1,8 @@ -package net.rk4z.fabricord.discord +package net.ririfa.fabricord.discord import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder import net.minecraft.server.network.ServerPlayerEntity -import net.rk4z.fabricord.Fabricord +import net.ririfa.fabricord.Fabricord object DiscordPlayerEventHandler { fun handleMCMessage(player: ServerPlayerEntity, message: String) { diff --git a/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageKey.kt b/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageKey.kt new file mode 100644 index 0000000..74a0385 --- /dev/null +++ b/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageKey.kt @@ -0,0 +1,23 @@ +package net.ririfa.fabricord.translation + +import net.minecraft.text.Text +import net.ririfa.langman.MessageKey + +open class FabricordMessageKey : MessageKey { + open class System : FabricordMessageKey() { + object Initializing : System() + + open class MissingRequiredProp : System() { + object ITEM1 : MissingRequiredProp() + object ITEM2 : MissingRequiredProp() + object ITEM3 : MissingRequiredProp() + } + + object Initialized : System() + + object ServerStart : System() + object ServerStop : System() + object PlayerJoin : System() + object PlayerLeave : System() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageProvider.kt b/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageProvider.kt new file mode 100644 index 0000000..de2ebf3 --- /dev/null +++ b/src/main/kotlin/net/ririfa/fabricord/translation/FabricordMessageProvider.kt @@ -0,0 +1,66 @@ +package net.ririfa.fabricord.translation + +import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.text.Text +import net.ririfa.langman.IMessageProvider +import net.ririfa.langman.LangMan +import net.ririfa.langman.MessageKey +import kotlin.reflect.full.isSubclassOf + +// Based https://github.com/SwiftStorm-Studio/SwiftBase/blob/main/integrations/fabric/src/main/kotlin/net/rk4z/s1/swiftbase/fabric/FabricPlayer.kt +class FabricordMessageProvider(val player: ServerPlayerEntity) : IMessageProvider { + private val languageManager: LangMan + get() { + if (!LangMan.isInitialized()) { + throw IllegalStateException("LangMan is not initialized but you are trying to use it.") + } + val languageManager = LangMan.Companion.getOrNull() + ?: throw IllegalStateException("LangMan is not initialized but you are trying to use it.") + + return languageManager + } + + override fun getLanguage(): String { + // https://maven.fabricmc.net/docs/yarn-1.21.4+build.8/net/minecraft/network/packet/c2s/common/SyncedClientOptions.html + // en_US -> en + return player.clientOptions.comp_1951.split("_")[0] + } + + override fun getMessage(key: MessageKey<*, *>, vararg args: Any): Text { + val messages = languageManager.messages + val expectedMKType = languageManager.expectedMKType + val textComponentFactory = languageManager.textComponentFactory + + require(key::class.isSubclassOf(expectedMKType)) { "Unexpected MessageKey type: ${key::class}. Expected: $expectedMKType" } + val lang = this.getLanguage() + val message = messages[lang]?.get(key) + val text = message?.let { String.format(it, *args) } ?: key.rc() + return textComponentFactory(text) + } + + override fun getRawMessage(key: MessageKey<*, *>): String { + val messages = languageManager.messages + val expectedMKType = languageManager.expectedMKType + + require(key::class.isSubclassOf(expectedMKType)) { "Unexpected MessageKey type: ${key::class}. Expected: $expectedMKType" } + val lang = this.getLanguage() + return messages[lang]?.get(key) ?: key.rc() + } + + override fun hasMessage(key: MessageKey<*, *>): Boolean { + val messages = languageManager.messages + val expectedMKType = languageManager.expectedMKType + + require(key::class.isSubclassOf(expectedMKType)) { "Unexpected MessageKey type: ${key::class}. Expected: $expectedMKType" } + val lang = this.getLanguage() + return messages[lang]?.containsKey(key) ?: false + } +} + +fun ServerPlayerEntity.adapt(): FabricordMessageProvider { + return FabricordMessageProvider(this) +} + +fun FabricordMessageProvider.getAPlayer(): ServerPlayerEntity { + return this.player +} \ No newline at end of file diff --git a/src/main/kotlin/net/rk4z/fabricord/utils/Utils.kt b/src/main/kotlin/net/ririfa/fabricord/utils/Utils.kt similarity index 95% rename from src/main/kotlin/net/rk4z/fabricord/utils/Utils.kt rename to src/main/kotlin/net/ririfa/fabricord/utils/Utils.kt index 6414bb0..bca6f54 100644 --- a/src/main/kotlin/net/rk4z/fabricord/utils/Utils.kt +++ b/src/main/kotlin/net/ririfa/fabricord/utils/Utils.kt @@ -1,7 +1,7 @@ -package net.rk4z.fabricord.utils +package net.ririfa.fabricord.utils import net.minecraft.server.network.ServerPlayerEntity -import net.rk4z.fabricord.Fabricord +import net.ririfa.fabricord.Fabricord import java.io.InputStream import java.nio.file.Files import java.nio.file.Path diff --git a/src/main/resources/assets/fabricord/lang/en.yml b/src/main/resources/assets/fabricord/lang/en.yml new file mode 100644 index 0000000..7177ecd --- /dev/null +++ b/src/main/resources/assets/fabricord/lang/en.yml @@ -0,0 +1,9 @@ +system: + initializing: "Initializing Fabricord..." + + missingrequiredprop: + - "Bot token or log channel ID is missing in config file." + - "Maybe you are running the mod for the first time?" + - "Please check the config file at %s" + + initialized: "Fabricord initialized successfully." \ No newline at end of file diff --git a/src/main/resources/assets/fabricord/lang/ja.yml b/src/main/resources/assets/fabricord/lang/ja.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/assets/fabricord/lang/ja.yml @@ -0,0 +1 @@ + diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 1e86222..6e0c4f7 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -17,7 +17,7 @@ "server": [ { "adapter": "kotlin", - "value": "net.rk4z.fabricord.Fabricord" + "value": "net.ririfa.fabricord.Fabricord" } ] }, diff --git a/src/main/resources/fabricord.mixins.json b/src/main/resources/fabricord.mixins.json index 0244dbd..7398b3b 100644 --- a/src/main/resources/fabricord.mixins.json +++ b/src/main/resources/fabricord.mixins.json @@ -1,6 +1,6 @@ { "required": true, - "package": "net.rk4z.fabricord.mixin", + "package": "net.ririfa.fabricord.mixin", "compatibilityLevel": "JAVA_21", "server": [ "PlayerAdvancementTrackerMixin",