Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply #9

Merged
merged 1 commit into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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") {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
package net.rk4z.fabricord
package net.ririfa.fabricord

import net.fabricmc.api.DedicatedServerModInitializer
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents
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()
Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.ririfa.fabricord.translation

import net.minecraft.text.Text
import net.ririfa.langman.MessageKey

open class FabricordMessageKey : MessageKey<FabricordMessageProvider, Text> {
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()
}
}
Original file line number Diff line number Diff line change
@@ -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<Text> {
private val languageManager: LangMan<FabricordMessageProvider, Text>
get() {
if (!LangMan.isInitialized()) {
throw IllegalStateException("LangMan is not initialized but you are trying to use it.")
}
val languageManager = LangMan.Companion.getOrNull<FabricordMessageProvider, Text>()
?: 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
}
Loading