diff --git a/gradle.properties b/gradle.properties index f3b42c54..511378bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.stdlib.default.dependency=false org.gradle.parallel=true -version=4.3.0 \ No newline at end of file +version=4.3.1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d997cfc6..b1b8ef56 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61a118f..b52fb7e7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 7453b1de..b9bb139f 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/6e1fb6d39530a68fd3d52eb274324eab142baa5b/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/gradlew.bat b/gradlew.bat index c4bdd3ab..24c62d56 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -23,8 +23,8 @@ @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line @@ -73,21 +73,10 @@ goto fail @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/config/configs/ConnectionMessageConfig.kt b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/config/configs/ConnectionMessageConfig.kt index 246ded8b..81207d7e 100644 --- a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/config/configs/ConnectionMessageConfig.kt +++ b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/config/configs/ConnectionMessageConfig.kt @@ -7,19 +7,4 @@ import org.spongepowered.configurate.objectmapping.meta.Comment data class ConnectionMessageConfig( @field:Comment("Whether join and quit messages are shown in chat at all.") val enabled: Boolean = true, - - @field:Comment( - "Automatically suppresses join and quit messages for regular players\n" + - "when too many connection events occur within one minute.\n" + - "Players with the permission 'surf.chat.connection.always-show'\n" + - "are exempt and their messages are always displayed." - ) - val autoDisableOnHighConnectionEventThreshold: Boolean = true, - - @field:Comment( - "Maximum number of combined join and quit events per minute before\n" + - "connection messages are suppressed automatically.\n" + - "Only relevant when 'autoDisableOnHighConnectionEventThreshold' is true." - ) - val connectionEventsPerMinuteThreshold: Int = 15, ) diff --git a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/hook/SettingsHook.kt b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/hook/SettingsHook.kt index 30993f78..17328616 100644 --- a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/hook/SettingsHook.kt +++ b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/hook/SettingsHook.kt @@ -10,4 +10,7 @@ object SettingsHook { fun hasChatPingsEnabled(playerUuid: UUID): Boolean = SurfSettingsApi.getSettingValue(playerUuid, SettingKeys.CHAT_PINGS) + + fun hasConnectionMessagesEnabled(playerUuid: UUID): Boolean = + SurfSettingsApi.getSettingValue(playerUuid, SettingKeys.CONNECTION_MESSAGES) } \ No newline at end of file diff --git a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/ConnectListener.kt b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/ConnectListener.kt index e980718f..a2182e6c 100644 --- a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/ConnectListener.kt +++ b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/ConnectListener.kt @@ -3,17 +3,19 @@ package dev.slne.surf.chat.paper.listener import dev.slne.surf.api.core.messages.adventure.buildText import dev.slne.surf.api.core.messages.adventure.sendText import dev.slne.surf.api.core.minimessage.miniMessage +import dev.slne.surf.api.paper.util.forEachPlayer import dev.slne.surf.chat.core.common.service.IgnoreService import dev.slne.surf.chat.paper.hook.LuckPermsHook import dev.slne.surf.chat.paper.hook.MiniPlaceholdersHook +import dev.slne.surf.chat.paper.hook.SettingsHook import dev.slne.surf.chat.paper.message.MessageFormatter import dev.slne.surf.chat.paper.permission.PermissionRegistry import dev.slne.surf.chat.paper.plugin -import dev.slne.surf.chat.paper.service.ConnectionMessageService import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent import kotlinx.coroutines.runBlocking import net.kyori.adventure.text.Component import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.player.PlayerJoinEvent @@ -28,17 +30,35 @@ object ConnectListener : Listener { } } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) fun onPlayerJoin(event: PlayerJoinEvent) { - ConnectionMessageService.recordEvent() + if (event.joinMessage() == null) { + return + } + + if (!plugin.connectionMessageConfig.enabled) { + return + } val alwaysShow = event.player.hasPermission(PermissionRegistry.CONNECTION_MESSAGE_ALWAYS_SHOW) - val shouldShowMessage = alwaysShow || ConnectionMessageService.shouldShowConnectionMessage() + val message = buildJoinMessage(event) - if (shouldShowMessage) { - event.joinMessage(buildJoinMessage(event)) + event.joinMessage(null) + + if (alwaysShow) { + forEachPlayer { + it.sendText { + append(message) + } + } } else { - event.joinMessage(null) + forEachPlayer { + if (plugin.checkSettingsHook() && SettingsHook.hasConnectionMessagesEnabled(event.player.uniqueId)) { + it.sendText { + append(message) + } + } + } } if (plugin.chatMotdConfig.enabled) { diff --git a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/DisconnectListener.kt b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/DisconnectListener.kt index 6d677f09..3b49240b 100644 --- a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/DisconnectListener.kt +++ b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/listener/DisconnectListener.kt @@ -3,33 +3,55 @@ package dev.slne.surf.chat.paper.listener import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.api.core.messages.adventure.buildText +import dev.slne.surf.api.core.messages.adventure.sendText import dev.slne.surf.api.core.minimessage.miniMessage +import dev.slne.surf.api.paper.util.forEachPlayer import dev.slne.surf.chat.core.common.service.IgnoreService import dev.slne.surf.chat.core.common.service.SpyService import dev.slne.surf.chat.paper.hook.LuckPermsHook +import dev.slne.surf.chat.paper.hook.SettingsHook import dev.slne.surf.chat.paper.message.MessageFormatter import dev.slne.surf.chat.paper.permission.PermissionRegistry import dev.slne.surf.chat.paper.plugin -import dev.slne.surf.chat.paper.service.ConnectionMessageService import net.kyori.adventure.text.Component import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.player.PlayerQuitEvent object DisconnectListener : Listener { - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) fun onDisconnect(event: PlayerQuitEvent) { MessageFormatter.dirty = true + + if (event.quitMessage() == null) { + return + } - ConnectionMessageService.recordEvent() + if (!plugin.connectionMessageConfig.enabled) { + return + } val alwaysShow = event.player.hasPermission(PermissionRegistry.CONNECTION_MESSAGE_ALWAYS_SHOW) - val shouldShowMessage = alwaysShow || ConnectionMessageService.shouldShowConnectionMessage() - if (shouldShowMessage) { - event.quitMessage(buildQuitMessage(event)) + event.quitMessage(null) + + val message = buildQuitMessage(event) + + if (alwaysShow) { + forEachPlayer { + it.sendText { + append(message) + } + } } else { - event.quitMessage(null) + forEachPlayer { + if (plugin.checkSettingsHook() && SettingsHook.hasConnectionMessagesEnabled(event.player.uniqueId)) { + it.sendText { + append(message) + } + } + } } } diff --git a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/service/ConnectionMessageService.kt b/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/service/ConnectionMessageService.kt deleted file mode 100644 index 9ab3e8b4..00000000 --- a/surf-chat-paper/src/main/kotlin/dev/slne/surf/chat/paper/service/ConnectionMessageService.kt +++ /dev/null @@ -1,86 +0,0 @@ -package dev.slne.surf.chat.paper.service - -import dev.slne.surf.chat.paper.plugin -import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue - -/** - * Service responsible for tracking recent player connection events in order to - * automatically disable connection messages during connection spikes. - * - * A sliding one-minute window is used to determine whether the configured - * threshold has been reached. - */ -object ConnectionMessageService { - private val lock = Any() - - /** - * Stores connection event timestamps in nanoseconds. - * - * Entries are ordered from oldest to newest. - */ - private val eventTimestamps = LongArrayFIFOQueue() - - /** - * Duration of the sliding rate-limit window. - */ - private const val WINDOW_NANOS = 60_000_000_000L // 1 minute - - /** - * Records a player connection event and updates the current sliding window state. - */ - fun recordEvent() { - val now = System.nanoTime() - - synchronized(lock) { - eventTimestamps.enqueue(now) - pruneOldTimestamps(now) - } - } - - /** - * Returns whether connection messages should currently be suppressed because - * the configured threshold has been reached. - * - * Old timestamps are cleaned up before evaluating the threshold. - * - * @return `true` if the amount of recent connection events is at or above - * the configured threshold, otherwise `false` - */ - fun isRateLimitExceeded(): Boolean { - val config = plugin.connectionMessageConfig - if (!config.autoDisableOnHighConnectionEventThreshold) return false - - return synchronized(lock) { - pruneOldTimestamps(System.nanoTime()) - eventTimestamps.size() >= config.connectionEventsPerMinuteThreshold - } - } - - /** - * Determines whether connection messages should currently be shown. - * - * Connection messages are hidden if: - * - the feature is disabled in the configuration - * - the connection event threshold has been reached - * - * @return `true` if connection messages should be displayed, - * otherwise `false` - */ - fun shouldShowConnectionMessage(): Boolean { - val config = plugin.connectionMessageConfig - return config.enabled && !isRateLimitExceeded() - } - - /** - * Removes timestamps that are outside the sliding one-minute window. - * - * @param now the current timestamp in nanoseconds - */ - private fun pruneOldTimestamps(now: Long) { - val cutoff = now - WINDOW_NANOS - - while (!eventTimestamps.isEmpty && eventTimestamps.firstLong() < cutoff) { - eventTimestamps.dequeueLong() - } - } -}