diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b5b1c7ad..d5da9f10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v3 with: - java-version: 16 + java-version: 17 distribution: temurin - name: Run Check uses: eskatos/gradle-command-action@v2 @@ -37,7 +37,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v3 with: - java-version: 16 + java-version: 17 distribution: temurin - name: Generate docs uses: eskatos/gradle-command-action@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5c4facdd..e3db56ad 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v3 with: - java-version: 16 + java-version: 17 distribution: temurin - name: Assemble Plugin diff --git a/.run/Run Server [mimic-bukkit].run.xml b/.run/Run Server [mimic-bukkit].run.xml new file mode 100644 index 00000000..8ece5bf9 --- /dev/null +++ b/.run/Run Server [mimic-bukkit].run.xml @@ -0,0 +1,20 @@ + + + + + + false + true + false + false + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e55bcc55..c3f215ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,20 @@ ## [Unreleased] +### Paper-first + +Keeping the plugin compatible both with Paper and Spigot consumes a lot of time. +Now, Mimic is Paper-first as it is the most popular platform. +This means that compatibility with Spigot is not guaranteed. + +To reduce the maintenance burden, support for versions older than 1.20 has been dropped. +Java 17 is required. + ### Added command `/mimic config` -Since now, it is possible to change Mimic config using commands in two ways: +> [!NOTE] +> This feature requires the CommandAPI plugin to be installed. + +Now it is possible to change Mimic config using commands in two ways: 1. Using interactive config `/mimic config`. Every option in the output is interactive, so you can change it just by mouse click. @@ -25,10 +37,10 @@ Since now, it is possible to change Mimic config using commands in two ways: ### Housekeeping -- Update required Java 1.8 → 16 -- Update Kotlin 1.6.20 → 1.9.20 +- Update required Java 1.8 → 17 +- Update Kotlin 1.6.20 → 2.1.20 - Replace ACF with CommandAPI -- Update Gradle 7.4.2 → 8.5 +- Update Gradle 7.4.2 → 8.13 - Update dependencies - Migrate to version catalogs diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 64fcc2b4..e63e03f7 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } kotlin { - jvmToolchain(16) + jvmToolchain(17) compilerOptions { freeCompilerArgs.add("-Xcontext-receivers") } @@ -15,7 +15,6 @@ dependencies { implementation(kotlin("serialization", version = kotlinVersion)) implementation(libs.dokka) implementation(libs.kotlinx.binaryCompatibilityValidator) - implementation(libs.gradleDownloadTask) } repositories { diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index b5a0fabf..105225c6 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -5,3 +5,5 @@ dependencyResolutionManagement { } } } + +rootProject.name = "buildSrc" diff --git a/buildSrc/src/main/kotlin/commons.gradle.kts b/buildSrc/src/main/kotlin/commons.gradle.kts index d3886bd9..84524062 100644 --- a/buildSrc/src/main/kotlin/commons.gradle.kts +++ b/buildSrc/src/main/kotlin/commons.gradle.kts @@ -18,30 +18,19 @@ tasks.test { } kotlin { - jvmToolchain(16) + jvmToolchain(17) explicitApi() compilerOptions { apiVersion = KotlinVersion.KOTLIN_1_9 languageVersion = KotlinVersion.KOTLIN_1_9 freeCompilerArgs.add("-Xjvm-default=all") - optIn.add("kotlin.RequiresOptIn") allWarningsAsErrors = System.getProperty("warningsAsErrors") == "true" - javaParameters = true - } -} - -// TODO: Remove after fix in BukkitGradle -// https://github.com/EndlessCodeGroup/BukkitGradle/issues/62 -afterEvaluate { - java { - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 } } dependencies { - implementation(kotlin("stdlib-jdk8")) + implementation(kotlin("stdlib")) testingDependencies() } diff --git a/buildSrc/src/main/kotlin/internal/Accessors.kt b/buildSrc/src/main/kotlin/internal/Accessors.kt new file mode 100644 index 00000000..60ed9bf4 --- /dev/null +++ b/buildSrc/src/main/kotlin/internal/Accessors.kt @@ -0,0 +1,8 @@ +package internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.getByName + +internal val Project.versionCatalogs: VersionCatalogsExtension + get() = extensions.getByName("versionCatalogs") diff --git a/buildSrc/src/main/kotlin/internal/libs.kt b/buildSrc/src/main/kotlin/internal/libs.kt index 87bb80bc..57946636 100644 --- a/buildSrc/src/main/kotlin/internal/libs.kt +++ b/buildSrc/src/main/kotlin/internal/libs.kt @@ -1,6 +1,5 @@ package internal -import gradle.kotlin.dsl.accessors._1f737d11fad22b9b058419dfc437a798.versionCatalogs import org.gradle.api.Project import org.gradle.api.artifacts.MinimalExternalModuleDependency import org.gradle.api.provider.Provider @@ -14,24 +13,24 @@ internal object libs { context(Project) val junit_bom - get() = get("junit-bom") + get() = lib("junit-bom") context(Project) val junit_jupiter - get() = get("junit-jupiter") + get() = lib("junit-jupiter") context(Project) val junit_jupiter_params - get() = get("junit-jupiter-params") + get() = lib("junit-jupiter-params") context(Project) val kotest_assertions - get() = get("kotest-assertions") + get() = lib("kotest-assertions") context(Project) val mockk - get() = get("mockk") + get() = lib("mockk") - private fun Project.get(alias: String): Provider = + private fun Project.lib(alias: String): Provider = versionCatalogs.named("libs").findLibrary(alias).get() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c6ff5b1e..d9ead5c6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,44 +1,63 @@ [versions] -kotlin = "1.9.20" -commandapi = "9.2.0" -junit = "5.10.1" +paper = "1.20-R0.1-SNAPSHOT" +kotlin = "2.1.20" +kotlinx-binaryCompatibilityValidator = "0.17.0" +kotlinx-serialization = "1.8.1" +dokka = "1.9.10" +annotations = "26.0.2" + +commandapi = "9.7.0" +bstats = "3.1.0" + +skillapi = "3.102" +battlelevels = "6.9.1" +mmocore = "1.12.1-SNAPSHOT" +mmoitems = "6.9.5-SNAPSHOT" +mythiclib = "1.6.2-SNAPSHOT" +heroes = "1.9.30-RELEASE" + +junit = "5.12.2" +kotest = "6.0.0.M3" +mockk = "1.14.0" + +gradlePlugin-bukkitgradle = "1.0.0" +gradlePlugin-shadow = "8.3.6" +gradlePlugin-versions = "0.52.0" [libraries] -spigot-api = "org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT" -bstats = "org.bstats:bstats-bukkit:3.0.2" -annotations = "org.jetbrains:annotations:24.1.0" -serialization-hocon = "org.jetbrains.kotlinx:kotlinx-serialization-hocon:1.6.1" -adventure = "net.kyori:adventure-platform-bukkit:4.3.1" +paperApi = { module = "io.papermc.paper:paper-api", version.ref = "paper" } +serialization-hocon = { module = "org.jetbrains.kotlinx:kotlinx-serialization-hocon", version.ref = "kotlinx-serialization" } +annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } -commandapi = { module = "dev.jorel:commandapi-bukkit-shade", version.ref = "commandapi" } +commandapi = { module = "dev.jorel:commandapi-bukkit-core", version.ref = "commandapi" } commandapi-kotlin = { module = "dev.jorel:commandapi-bukkit-kotlin", version.ref = "commandapi" } +bstats = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } -rpgplugins-skillapi = "com.sucy:SkillAPI:3.102" -rpgplugins-battlelevels = "me.robin:BattleLevels:6.9.1" -rpgplugins-mmocore = "net.Indyuce:MMOCore:1.9.2" -rpgplugins-mmoitems = "net.Indyuce:MMOItems:6.7.2" -rpgplugins-mythiclib = "io.lumine:MythicLib-dist:1.4" -rpgplugins-heroes = "com.herocraftonline.heroes:Heroes:1.9.30-RELEASE" +rpgplugins-skillapi = { module = "com.sucy:SkillAPI", version.ref = "skillapi" } +rpgplugins-battlelevels = { module = "me.robin:BattleLevels", version.ref = "battlelevels" } +rpgplugins-mmocore = { module = "net.Indyuce:MMOCore-API", version.ref = "mmocore" } +rpgplugins-mmoitems = { module = "net.Indyuce:MMOItems-API", version.ref = "mmoitems" } +rpgplugins-mythiclib = { module = "io.lumine:MythicLib-dist", version.ref = "mythiclib" } +rpgplugins-heroes = { module = "com.herocraftonline.heroes:Heroes", version.ref = "heroes" } # Test dependencies junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" } -kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" -mockk = "io.mockk:mockk:1.13.8" +kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } # Build dependencies -dokka = "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10" -kotlinx-binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.13.2" -gradleDownloadTask = "de.undercouch:gradle-download-task:5.5.0" +dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } +kotlinx-binaryCompatibilityValidator = { module = "org.jetbrains.kotlinx:binary-compatibility-validator", version.ref = "kotlinx-binaryCompatibilityValidator" } [plugins] -bukkitgradle = "ru.endlesscode.bukkitgradle:0.10.1" -shadow = "com.github.johnrengelman.shadow:8.1.1" -versions = "com.github.ben-manes.versions:0.50.0" +bukkitgradle = { id = "ru.endlesscode.bukkitgradle", version.ref = "gradlePlugin-bukkitgradle" } +shadow = { id = "com.gradleup.shadow", version.ref = "gradlePlugin-shadow" } +versions = { id = "com.github.ben-manes.versions", version.ref = "gradlePlugin-versions" } [bundles] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..9bbc975c 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 1af9e093..37f853b1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..faf93008 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/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/. @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -203,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +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 @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +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 diff --git a/mimic-bukkit-api/build.gradle.kts b/mimic-bukkit-api/build.gradle.kts index 0528f11b..04aca800 100644 --- a/mimic-bukkit-api/build.gradle.kts +++ b/mimic-bukkit-api/build.gradle.kts @@ -6,11 +6,11 @@ plugins { description = "Abstraction API for Bukkit RPG plugins" repositories { - maven(url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots") + maven("https://repo.papermc.io/repository/maven-public/") } dependencies { api(projects.mimicApi) compileOnly(libs.annotations) - compileOnly(libs.spigot.api) { isTransitive = false } + compileOnly(libs.paperApi) } diff --git a/mimic-bukkit/api/mimic-bukkit.api b/mimic-bukkit/api/mimic-bukkit.api index 51d172e1..b44f686a 100644 --- a/mimic-bukkit/api/mimic-bukkit.api +++ b/mimic-bukkit/api/mimic-bukkit.api @@ -279,24 +279,24 @@ public final class ru/endlesscode/mimic/impl/skillapi/SkillApiLevelSystem$Compan public final class ru/endlesscode/mimic/impl/vanilla/ItemMetaPayload { public static final field Companion Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload$Companion; public fun ()V - public fun (Ljava/lang/String;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; + public fun (Lnet/kyori/adventure/text/Component;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;)V + public synthetic fun (Lnet/kyori/adventure/text/Component;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lnet/kyori/adventure/text/Component; public final fun component2 ()Ljava/util/List; public final fun component3 ()Z public final fun component4 ()I public final fun component5 ()Ljava/lang/Integer; public final fun component6 ()Ljava/util/Map; public final fun component7 ()Ljava/util/Set; - public final fun copy (Ljava/lang/String;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;)Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload; - public static synthetic fun copy$default (Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload;Ljava/lang/String;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;ILjava/lang/Object;)Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload; + public final fun copy (Lnet/kyori/adventure/text/Component;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;)Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload; + public static synthetic fun copy$default (Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload;Lnet/kyori/adventure/text/Component;Ljava/util/List;ZILjava/lang/Integer;Ljava/util/Map;Ljava/util/Set;ILjava/lang/Object;)Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload; public fun equals (Ljava/lang/Object;)Z public final fun getCustomModelData ()Ljava/lang/Integer; public final fun getDamage ()I public final fun getEnchantments ()Ljava/util/Map; public final fun getFlags ()Ljava/util/Set; public final fun getLore ()Ljava/util/List; - public final fun getName ()Ljava/lang/String; + public final fun getName ()Lnet/kyori/adventure/text/Component; public fun hashCode ()I public final fun isUnbreakable ()Z public static final fun of (Ljava/lang/Object;)Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload; @@ -312,7 +312,6 @@ public final class ru/endlesscode/mimic/impl/vanilla/ItemMetaPayload$$serializer public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lru/endlesscode/mimic/impl/vanilla/ItemMetaPayload;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class ru/endlesscode/mimic/impl/vanilla/ItemMetaPayload$Companion { diff --git a/mimic-bukkit/build.gradle.kts b/mimic-bukkit/build.gradle.kts index 9622f60b..ca85928d 100644 --- a/mimic-bukkit/build.gradle.kts +++ b/mimic-bukkit/build.gradle.kts @@ -1,5 +1,6 @@ import ru.endlesscode.bukkitgradle.dependencies.aikar import ru.endlesscode.bukkitgradle.dependencies.codemc +import ru.endlesscode.bukkitgradle.dependencies.papermc plugins { commons @@ -9,28 +10,50 @@ plugins { kotlin("plugin.serialization") } -description = "Bukkit plugin with implementations of Mimic APIs" +description = "Bukkit plugin implementing Mimic APIs" bukkit { - meta { + apiVersion = "1.20" + + plugin { name = "Mimic" main = "ru.endlesscode.mimic.MimicPlugin" - apiVersion = "1.13" authors = listOf("osipxd", "EndlessCodeGroup") - url = "https://github.com/EndlessCodeGroup/Mimic" + website = "https://github.com/EndlessCodeGroup/Mimic" + softDepend = listOf("CommandAPI") + loadBefore = listOf( + "SkillAPI", + "BattleLevels", + "CustomItems", + "MMOCore", + "MMOItems", + "Heroes", + "QuantumRPG", + ) } server { - setCore("paper") + version = "1.21.5" eula = true } } +tasks.runServer { + downloadPlugins { + github("CommandAPI", "CommandAPI", "10.0.0", "CommandAPI-10.0.0.jar") + } +} + repositories { + papermc() maven(url = "https://gitlab.com/endlesscodegroup/mvn-repo/raw/master/") maven(url = "https://mvn.lumine.io/repository/maven-public/") { content { includeModule("me.robin", "BattleLevels") + } + } + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") { + content { includeGroup("net.Indyuce") includeModule("io.lumine", "MythicLib-dist") } @@ -45,23 +68,22 @@ repositories { dependencies { api(projects.mimicBukkitApi) - compileOnly(libs.spigot.api) { isTransitive = false } + compileOnly(libs.paperApi) compileOnly(libs.annotations) implementation(libs.bstats) implementation(libs.serialization.hocon) - implementation(libs.commandapi) - implementation(libs.commandapi.kotlin) - implementation(libs.adventure) + compileOnly(libs.commandapi) + compileOnly(libs.commandapi.kotlin) compileOnly(libs.bundles.rpgplugins) { isTransitive = false } // From libs/ directory compileOnly(":CustomItemsAPI") compileOnly(":QuantumRPG:5.10.2") - compileOnly(":NexEngine:2.0.3") // Do not update NexEngine. QuantumRpgWrapper cannot compile with higher version + compileOnly(":NexEngine:2.0.3") // Do not update NexEngine. QuantumRpgWrapper cannot compile with a higher version - testImplementation(libs.spigot.api) + testImplementation(libs.paperApi) testImplementation(libs.rpgplugins.skillapi) } @@ -71,6 +93,12 @@ kotlin { } } +tasks.test { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(17) + } +} + tasks.shadowJar { dependencies { exclude(dependency("org.jetbrains:annotations:.*")) @@ -80,9 +108,6 @@ tasks.shadowJar { relocate("kotlin", "$shadePackage.kotlin") relocate("org.bstats", "$shadePackage.bstats") relocate("com.typesafe.config", "$shadePackage.hocon") - relocate("dev.jorel.commandapi", "$shadePackage.commandapi") - relocate("net.kyori.adventure", "$shadePackage.adventure") - relocate("net.kyori.examination", "$shadePackage.examination") exclude("META-INF/*.kotlin_module") exclude("META-INF/com.android.tools/**") diff --git a/mimic-bukkit/libs/QuantumRPG-5.10.2.jar b/mimic-bukkit/libs/QuantumRPG-5.10.2.jar index acf19fc5..21e82345 100644 Binary files a/mimic-bukkit/libs/QuantumRPG-5.10.2.jar and b/mimic-bukkit/libs/QuantumRPG-5.10.2.jar differ diff --git a/mimic-bukkit/src/main/kotlin/MimicPlugin.kt b/mimic-bukkit/src/main/kotlin/MimicPlugin.kt index a4e6773f..047e9059 100644 --- a/mimic-bukkit/src/main/kotlin/MimicPlugin.kt +++ b/mimic-bukkit/src/main/kotlin/MimicPlugin.kt @@ -19,9 +19,6 @@ package ru.endlesscode.mimic -import dev.jorel.commandapi.CommandAPI -import dev.jorel.commandapi.CommandAPIBukkitConfig -import net.kyori.adventure.platform.bukkit.BukkitAudiences import org.bstats.bukkit.Metrics import org.bstats.charts.AdvancedPie import org.bstats.charts.SimplePie @@ -32,7 +29,7 @@ import org.bukkit.plugin.java.JavaPlugin import ru.endlesscode.mimic.bukkit.loadAll import ru.endlesscode.mimic.bukkit.register import ru.endlesscode.mimic.classes.BukkitClassSystem -import ru.endlesscode.mimic.command.registerCommand +import ru.endlesscode.mimic.command.MimicCommands import ru.endlesscode.mimic.config.MimicConfig import ru.endlesscode.mimic.impl.battlelevels.BattleLevelsLevelSystem import ru.endlesscode.mimic.impl.customitems.CustomItemsRegistry @@ -60,34 +57,25 @@ import ru.endlesscode.mimic.util.checkClassesLoaded /** Main class of the plugin. */ public class MimicPlugin : JavaPlugin() { - private val isReleased = !description.version.endsWith("-SNAPSHOT") + private val isReleased = !pluginMeta.version.endsWith("-SNAPSHOT") private val config: MimicConfig by lazy { MimicConfig(this) } private val mimic: Mimic by lazy { MimicImpl(servicesManager, config) } - private var audiences: BukkitAudiences? = null + private val commands: MimicCommands by lazy { MimicCommands() } private inline val servicesManager get() = server.servicesManager private inline val pluginManager get() = server.pluginManager override fun onLoad() { Log.init(logger, debug = !isReleased) - CommandAPI.onLoad(CommandAPIBukkitConfig(this)) servicesManager.register(mimic, this) hookDefaultServices() } override fun onEnable() { - CommandAPI.onEnable() - audiences = BukkitAudiences.create(this) - if (isReleased) initMetrics() - registerCommand( - mimic = mimic, - config = config, - pluginFullName = description.fullName, - audiences = checkNotNull(audiences), - ) + commands.register(this, mimic, config) pluginManager.registerEvents(ServicesRegistrationListener(servicesManager, mimic), this) } @@ -193,9 +181,6 @@ public class MimicPlugin : JavaPlugin() { } override fun onDisable() { - CommandAPI.unregister("mimic") - CommandAPI.onDisable() - audiences?.close() - audiences = null + commands.unregister() } } diff --git a/mimic-bukkit/src/main/kotlin/ServicesRegistrationListener.kt b/mimic-bukkit/src/main/kotlin/ServicesRegistrationListener.kt index b26183b1..eb83d7fd 100644 --- a/mimic-bukkit/src/main/kotlin/ServicesRegistrationListener.kt +++ b/mimic-bukkit/src/main/kotlin/ServicesRegistrationListener.kt @@ -26,7 +26,7 @@ internal class ServicesRegistrationListener( Log.w( """ Service ${serviceClass.name} with id '${service.id}' registered in deprecated way. - Please ask the ${plugin.name} authors (${plugin.description.authors.joinToString()}) to migrate + Please ask the ${plugin.name} authors (${plugin.pluginMeta.authors.joinToString()}) to migrate to the new service registration API introduced in Mimic v0.7: https://github.com/EndlessCodeGroup/Mimic/releases/tag/v0.7 """.trimIndent() diff --git a/mimic-bukkit/src/main/kotlin/command/ClassSystemSubcommand.kt b/mimic-bukkit/src/main/kotlin/command/ClassSystemSubcommand.kt index a287277a..22f00eac 100644 --- a/mimic-bukkit/src/main/kotlin/command/ClassSystemSubcommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/ClassSystemSubcommand.kt @@ -23,7 +23,12 @@ import dev.jorel.commandapi.executors.PlayerCommandExecutor import dev.jorel.commandapi.kotlindsl.greedyStringArgument import dev.jorel.commandapi.kotlindsl.playerArgument import dev.jorel.commandapi.kotlindsl.subcommand +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor import ru.endlesscode.mimic.Mimic +import ru.endlesscode.mimic.internal.append +import ru.endlesscode.mimic.internal.appendLine +import ru.endlesscode.mimic.internal.text /** * Commands to deal with class systems. @@ -51,11 +56,15 @@ private fun infoCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { player, val target = args.getOrDefaultUnchecked(TARGET, player) val provider = mimic.getClassSystemProvider() val system = provider.getSystem(target) - player.send( - "&3System: &7${provider.id}", - "&3Classes: &7${system.classes}", - "&3Primary: &7${system.primaryClass}", - ) + + val message = text { + appendStats( + "System" to provider.id, + "Classes" to system.classes.toString(), + "Primary" to system.primaryClass.toString(), + ) + } + player.sendMessage(message) } private fun checkCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { player, args -> @@ -65,14 +74,26 @@ private fun checkCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { player, val system = mimic.getClassSystem(target) val hasAllClasses = system.hasAllClasses(classes) val hasAnyOfClasses = system.hasAnyOfClasses(classes) - target.send( - "&6Player '${target.name}':", - "&6- has any of: ${hasAnyOfClasses.toChatMessage()}", - "&6- has all: ${hasAllClasses.toChatMessage()}", - ) + + val message = text { + color(NamedTextColor.GOLD) + appendLine("Player '${target.name}':") + append("- has any of: ") + appendStatus(hasAnyOfClasses) + appendLine() + append("- has all: ") + appendStatus(hasAllClasses) + } + target.sendMessage(message) } -private fun Boolean.toChatMessage(): String = if (this) "&ayes" else "&cno" +private fun TextComponent.Builder.appendStatus(status: Boolean) { + if (status) { + append("yes", NamedTextColor.GREEN) + } else { + append("no", NamedTextColor.RED) + } +} private const val TARGET = "target" private const val CLASSES = "classes" diff --git a/mimic-bukkit/src/main/kotlin/command/ConfigCommand.kt b/mimic-bukkit/src/main/kotlin/command/ConfigCommand.kt index 4cc12340..eb8cb832 100644 --- a/mimic-bukkit/src/main/kotlin/command/ConfigCommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/ConfigCommand.kt @@ -6,7 +6,6 @@ import dev.jorel.commandapi.kotlindsl.anyExecutor import dev.jorel.commandapi.kotlindsl.multiLiteralArgument import dev.jorel.commandapi.kotlindsl.stringArgument import dev.jorel.commandapi.kotlindsl.subcommand -import net.kyori.adventure.platform.bukkit.BukkitAudiences import org.bukkit.command.CommandSender import ru.endlesscode.mimic.ExperimentalMimicApi import ru.endlesscode.mimic.Mimic @@ -24,11 +23,10 @@ import ru.endlesscode.mimic.config.MimicConfig internal fun CommandAPICommand.configSubcommand( mimic: Mimic, config: MimicConfig, - audiences: BukkitAudiences, ) = subcommand("config") { val showConfig = { sender: CommandSender -> val message = buildConfigMessage(mimic, config) - audiences.sender(sender).sendMessage(message) + sender.sendMessage(message) } withShortDescription("Show Mimic config") diff --git a/mimic-bukkit/src/main/kotlin/command/ConfigMessage.kt b/mimic-bukkit/src/main/kotlin/command/ConfigMessage.kt index 2b87c7fc..ffcd005c 100644 --- a/mimic-bukkit/src/main/kotlin/command/ConfigMessage.kt +++ b/mimic-bukkit/src/main/kotlin/command/ConfigMessage.kt @@ -12,10 +12,10 @@ import ru.endlesscode.mimic.config.StringSetConfigProperty import ru.endlesscode.mimic.internal.append import ru.endlesscode.mimic.internal.appendClickable import ru.endlesscode.mimic.internal.appendLine -import ru.endlesscode.mimic.internal.buildTextComponent +import ru.endlesscode.mimic.internal.text @OptIn(ExperimentalMimicApi::class) -internal fun buildConfigMessage(mimic: Mimic, config: MimicConfig): TextComponent = buildTextComponent { +internal fun buildConfigMessage(mimic: Mimic, config: MimicConfig): TextComponent = text { appendLine("----[ Mimic Config ]----") appendSelectablePropertyConfig( property = MimicConfig.LEVEL_SYSTEM, @@ -53,7 +53,7 @@ private fun TextComponent.Builder.appendSelectablePropertyConfig( availableOptions, hint = "Click to select", color = NamedTextColor.GRAY, - resolveCommand = { "/mimic config ${property.path} $it" }, + resolveCommand = { "/$COMMAND_NAME config ${property.path} $it" }, ) } @@ -76,14 +76,14 @@ private fun TextComponent.Builder.appendSetPropertyConfig( hint = "Click to add", color = NamedTextColor.GRAY, nonClickableOptions = permanentOptions, - resolveCommand = { "/mimic config ${property.path} add $it" }, + resolveCommand = { "/$COMMAND_NAME config ${property.path} add $it" }, ) appendLine() appendPropertyPath(property) append("[") appendSelectableOptions(values, hint = "Click to remove") { - "/mimic config ${property.path} remove $it" + "/$COMMAND_NAME config ${property.path} remove $it" } append("]") } @@ -103,7 +103,7 @@ private fun TextComponent.Builder.appendSelectableOptions( resolveCommand: (String) -> String, ) = append( options.mapIndexed { index, option -> - buildTextComponent { + text { color(color) if (index != 0) append(", ") if (option !in nonClickableOptions) { diff --git a/mimic-bukkit/src/main/kotlin/command/FallbackCommand.kt b/mimic-bukkit/src/main/kotlin/command/FallbackCommand.kt new file mode 100644 index 00000000..c393b011 --- /dev/null +++ b/mimic-bukkit/src/main/kotlin/command/FallbackCommand.kt @@ -0,0 +1,41 @@ +package ru.endlesscode.mimic.command + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.event.ClickEvent +import net.kyori.adventure.text.event.HoverEvent +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration +import org.bukkit.command.CommandSender +import org.bukkit.command.defaults.BukkitCommand +import ru.endlesscode.mimic.internal.append +import ru.endlesscode.mimic.internal.appendLine +import ru.endlesscode.mimic.internal.text + +internal class FallbackCommand( + private val pluginFullName: String +) : BukkitCommand( + COMMAND_NAME, + COMMAND_INFO_DESCRIPTION, + "/$COMMAND_NAME", + emptyList(), +) { + + override fun execute(sender: CommandSender, commandLabel: String, args: Array?): Boolean { + val message = text { + appendLine(pluginFullName, NamedTextColor.GREEN) + color(NamedTextColor.GRAY) + append("Install ") + append(commandApiLink()) + append(" to unlock all Mimic commands.") + } + sender.sendMessage(message) + return true + } + + private fun commandApiLink() = text { + color(NamedTextColor.YELLOW) + append("CommandAPI", TextDecoration.UNDERLINED) + hoverEvent(HoverEvent.showText(Component.text("Open CommandAPI website"))) + clickEvent(ClickEvent.openUrl("https://docs.commandapi.dev/user-setup/install")) + } +} diff --git a/mimic-bukkit/src/main/kotlin/command/InventorySubcommand.kt b/mimic-bukkit/src/main/kotlin/command/InventorySubcommand.kt index 01eb2401..2ed4e83e 100644 --- a/mimic-bukkit/src/main/kotlin/command/InventorySubcommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/InventorySubcommand.kt @@ -6,6 +6,7 @@ import dev.jorel.commandapi.kotlindsl.playerExecutor import dev.jorel.commandapi.kotlindsl.subcommand import ru.endlesscode.mimic.ExperimentalMimicApi import ru.endlesscode.mimic.Mimic +import ru.endlesscode.mimic.internal.text /** * Commands to deal with inventory provider. @@ -24,12 +25,16 @@ internal fun CommandAPICommand.inventorySubcommand(mimic: Mimic) = subcommand("i val target = args.getOrDefaultUnchecked(TARGET, sender) val provider = mimic.getPlayerInventoryProvider() val inventory = provider.getSystem(target) - sender.send( - "&3Inventory provider: &7${provider.id}", - "&3Count of Equipped: &7%d".format(inventory.equippedItems.size), - "&3Count of Stored: &7%d".format(inventory.storedItems.size), - "&3Total Count: &7%d".format(inventory.items.size), - ) + + val message = text { + appendStats( + "Inventory provider" to provider.id, + "Count of Equipped" to inventory.equippedItems.size.toString(), + "Count of Stored" to inventory.storedItems.size.toString(), + "Total Count" to inventory.items.size.toString(), + ) + } + sender.sendMessage(message) } } } diff --git a/mimic-bukkit/src/main/kotlin/command/ItemsSubcommand.kt b/mimic-bukkit/src/main/kotlin/command/ItemsSubcommand.kt index af6d31e4..487090a0 100644 --- a/mimic-bukkit/src/main/kotlin/command/ItemsSubcommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/ItemsSubcommand.kt @@ -23,9 +23,14 @@ import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.arguments.ArgumentSuggestions import dev.jorel.commandapi.executors.CommandExecutor import dev.jorel.commandapi.kotlindsl.* +import net.kyori.adventure.text.format.NamedTextColor import org.bukkit.entity.Player import ru.endlesscode.mimic.impl.mimic.MimicItemsRegistry +import ru.endlesscode.mimic.internal.append +import ru.endlesscode.mimic.internal.appendLine +import ru.endlesscode.mimic.internal.text import ru.endlesscode.mimic.items.BukkitItemsRegistry +import ru.endlesscode.mimic.items.unwrap /** * Commands to deal with items registries @@ -59,7 +64,7 @@ internal fun CommandAPICommand.itemsSubcommand(itemsRegistry: BukkitItemsRegistr playerExecutor { sender, args -> val item: String by args val isSame = itemsRegistry.isSameItem(sender.inventory.itemInMainHand, item) - sender.send("&6Item in hand and '$item' %s same.".format(if (isSame) "are" else "aren't")) + sender.sendMessage(successText("Item in hand and '$item' %s same.".format(if (isSame) "are" else "aren't"))) } } @@ -67,7 +72,7 @@ internal fun CommandAPICommand.itemsSubcommand(itemsRegistry: BukkitItemsRegistr withShortDescription("Prints ID of item in hand") playerExecutor { sender, _ -> val id = itemsRegistry.getItemId(sender.inventory.itemInMainHand) - sender.send("&6Id of item in hand is '$id'") + sender.sendMessage(successText("Id of item in hand is '$id'")) } } @@ -77,31 +82,35 @@ internal fun CommandAPICommand.itemsSubcommand(itemsRegistry: BukkitItemsRegistr anyExecutor { sender, args -> val item: String by args val itemExists = itemsRegistry.isItemExists(item) - sender.send("&6Item with id '$item'%s exists".format(if (itemExists) "" else " isn't")) + sender.sendMessage(successText("Item with id '$item'%s exists".format(if (itemExists) "" else " isn't"))) } } } // We can use only greedy string if we need to allow colons because it requires quoting in non-greedy strings. // https://github.com/Mojang/brigadier/blob/cf754c4ef654160dca946889c11941634c5db3d5/src/main/java/com/mojang/brigadier/StringReader.java#L169 -private fun CommandAPICommand.itemArgument(itemsRegistry: BukkitItemsRegistry) = greedyStringArgument(ITEM) { - replaceSuggestions(ArgumentSuggestions.stringCollection { itemsRegistry.knownIds }) +private fun CommandAPICommand.itemArgument(itemRegistry: BukkitItemsRegistry) = greedyStringArgument(ITEM) { + replaceSuggestions(ArgumentSuggestions.stringCollection { itemRegistry.knownIds }) } -private fun infoExecutor(itemsRegistry: BukkitItemsRegistry) = CommandExecutor { sender, _ -> - val registries = (itemsRegistry as? MimicItemsRegistry)?.providers - .orEmpty() - .map { it.provider } - .map { " &f${it.id}: &7${it.knownIds.size}" } +private fun infoExecutor(itemRegistry: BukkitItemsRegistry) = CommandExecutor { sender, _ -> + val providers = (itemRegistry.unwrap() as? MimicItemsRegistry)?.providers.orEmpty().map { it.provider } - sender.send( - "&3Items Service: &7${itemsRegistry.id}", - "&3Known IDs amount: &7${itemsRegistry.knownIds.size}" - ) - sender.send(registries) + val message = text { + appendStats( + "Item Registry" to itemRegistry.id, + "Known IDs amount" to itemRegistry.knownIds.size.toString(), + ) + + for (provider in providers) { + append(" ${provider.id}: ", NamedTextColor.WHITE) + appendLine(provider.knownIds.size.toString(), NamedTextColor.GRAY) + } + } + sender.sendMessage(message) } -private fun giveExecutor(itemsRegistry: BukkitItemsRegistry) = CommandExecutor { sender, args -> +private fun giveExecutor(itemRegistry: BukkitItemsRegistry) = CommandExecutor { sender, args -> val target: Player by args val amount = args.getOrDefaultUnchecked(AMOUNT, 1) // We can use only one greedy string at the end, so we read item and its payload from the same argument @@ -109,16 +118,15 @@ private fun giveExecutor(itemsRegistry: BukkitItemsRegistry) = CommandExecutor { val item = itemParts.first() val payload = itemParts.getOrNull(1) - val itemStack = itemsRegistry.getItem(item, payload, amount) + val itemStack = itemRegistry.getItem(item, payload, amount) if (itemStack != null) { target.inventory.addItem(itemStack) - sender.send("&6Gave ${itemStack.amount} [$item] to ${target.name}.") + sender.sendMessage(successText("Gave ${itemStack.amount} [$item] to ${target.name}.")) } else { - sender.send("&cUnknown item '$item'.") + sender.sendMessage(errorText("Unknown item '$item'")) } } private const val TARGET = "target" private const val ITEM = "item" private const val AMOUNT = "amount" -private const val PAYLOAD = "payload" diff --git a/mimic-bukkit/src/main/kotlin/command/LevelSystemSubcommand.kt b/mimic-bukkit/src/main/kotlin/command/LevelSystemSubcommand.kt index ed2a4e2e..3c6e036a 100644 --- a/mimic-bukkit/src/main/kotlin/command/LevelSystemSubcommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/LevelSystemSubcommand.kt @@ -24,9 +24,11 @@ import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.arguments.ArgumentSuggestions import dev.jorel.commandapi.executors.PlayerCommandExecutor import dev.jorel.commandapi.kotlindsl.* +import net.kyori.adventure.text.TextComponent import org.bukkit.command.CommandSender import ru.endlesscode.mimic.Mimic import ru.endlesscode.mimic.internal.Log +import ru.endlesscode.mimic.internal.text import ru.endlesscode.mimic.level.BukkitLevelSystem import kotlin.math.roundToInt @@ -97,12 +99,17 @@ private fun infoCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { sender, val target = args.getOrDefaultUnchecked(TARGET, sender) val provider = mimic.getLevelSystemProvider() val system = provider.getSystem(target) - sender.send( - "&3System: &7${provider.id}", - "&3Level: &7%.2f".format(system.level + system.fractionalExp), - "&3Exp: &7%.1f &8| &3To next level: &7%.1f".format(system.exp, system.expToNextLevel), - "&3Total exp: &7%.1f".format(system.totalExp) - ) + + val message = text { + appendStats( + "System" to provider.id, + "Level" to "%.2f".format(system.level + system.fractionalExp), + "Exp" to "%.1f".format(system.exp), + "Exp to next level" to "%.1f".format(system.expToNextLevel), + "Total exp" to "%.1f".format(system.totalExp) + ) + } + target.sendMessage(message) } private fun setCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { sender, args -> @@ -159,7 +166,7 @@ private inline fun catchUnsupported(block: () -> Unit) { } private fun BukkitLevelSystem.printNewStats(sender: CommandSender) { - sender.send("&6New ${player.name}'s stats: $level LVL, %.1f XP".format(exp)) + sender.sendMessage(successText("New ${player.name}'s stats: $level LVL, %.1f XP".format(exp))) } private fun hasCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { sender, args -> @@ -167,9 +174,9 @@ private fun hasCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { sender, a val type = args.getOrDefaultRaw(TYPE, TYPE_LVL) val target = args.getOrDefaultUnchecked(TARGET, sender) - fun buildMessage(has: Boolean, valueType: String): String { + fun buildMessage(has: Boolean, valueType: String): TextComponent { val hasOrNot = if (has) "has" else "has not" - return "&6${target.name} $hasOrNot $amount $valueType." + return successText("${target.name} $hasOrNot $amount $valueType.") } val system = mimic.getLevelSystem(target) @@ -179,7 +186,7 @@ private fun hasCommandExecutor(mimic: Mimic) = PlayerCommandExecutor { sender, a TYPE_TOTAL -> buildMessage(system.hasExpTotal(amount), "total experience") else -> error("Unexpected type: $type") } - sender.send(message) + sender.sendMessage(message) } private const val TARGET = "target" diff --git a/mimic-bukkit/src/main/kotlin/command/MainCommand.kt b/mimic-bukkit/src/main/kotlin/command/MainCommand.kt index c81b2046..d767346d 100644 --- a/mimic-bukkit/src/main/kotlin/command/MainCommand.kt +++ b/mimic-bukkit/src/main/kotlin/command/MainCommand.kt @@ -2,47 +2,48 @@ package ru.endlesscode.mimic.command import dev.jorel.commandapi.executors.CommandExecutor import dev.jorel.commandapi.kotlindsl.commandAPICommand -import net.kyori.adventure.platform.bukkit.BukkitAudiences import net.kyori.adventure.text.format.NamedTextColor import ru.endlesscode.mimic.Mimic import ru.endlesscode.mimic.config.MimicConfig import ru.endlesscode.mimic.internal.append import ru.endlesscode.mimic.internal.appendClickable import ru.endlesscode.mimic.internal.appendLine -import ru.endlesscode.mimic.internal.buildTextComponent +import ru.endlesscode.mimic.internal.text + +internal const val COMMAND_NAME = "mimic" +internal const val COMMAND_INFO_DESCRIPTION = "Show info about Mimic" /** Registers command '/mimic' and all subcommands. */ internal fun registerCommand( mimic: Mimic, config: MimicConfig, pluginFullName: String, - audiences: BukkitAudiences, -) = commandAPICommand("mimic") { +) = commandAPICommand(COMMAND_NAME) { withPermission("mimic.admin") - withShortDescription("Show info about Mimic") - executes(infoExecutor(audiences, pluginFullName)) + withShortDescription(COMMAND_INFO_DESCRIPTION) + executes(infoExecutor(pluginFullName)) - configSubcommand(mimic, config, audiences) + configSubcommand(mimic, config) levelSystemSubcommand(mimic) classSystemSubcommand(mimic) inventorySubcommand(mimic) itemsSubcommand(mimic.getItemsRegistry()) } -private fun infoExecutor(audiences: BukkitAudiences, pluginFullName: String) = CommandExecutor { sender, _ -> - val message = buildTextComponent { +private fun infoExecutor(pluginFullName: String) = CommandExecutor { sender, _ -> + val message = text { appendLine(pluginFullName, NamedTextColor.GREEN) color(NamedTextColor.GRAY) append("Use ") append(createClickableCommand()) append(" to see or change configs") } - audiences.sender(sender).sendMessage(message) + sender.sendMessage(message) } -private fun createClickableCommand() = buildTextComponent { +private fun createClickableCommand() = text { color(NamedTextColor.YELLOW) appendClickable(CONFIG_COMMAND, "Click to execute", CONFIG_COMMAND) } -private const val CONFIG_COMMAND = "/mimic config" +private const val CONFIG_COMMAND = "/$COMMAND_NAME config" diff --git a/mimic-bukkit/src/main/kotlin/command/MimicCommands.kt b/mimic-bukkit/src/main/kotlin/command/MimicCommands.kt new file mode 100644 index 00000000..d9d41469 --- /dev/null +++ b/mimic-bukkit/src/main/kotlin/command/MimicCommands.kt @@ -0,0 +1,48 @@ +package ru.endlesscode.mimic.command + +import dev.jorel.commandapi.CommandAPI +import org.bukkit.plugin.java.JavaPlugin +import ru.endlesscode.mimic.Mimic +import ru.endlesscode.mimic.config.MimicConfig +import ru.endlesscode.mimic.internal.Log + +internal class MimicCommands { + + private var registered = false + + fun register( + plugin: JavaPlugin, + mimic: Mimic, + config: MimicConfig, + ) { + val commandApiPlugin = plugin.server.pluginManager.getPlugin("CommandAPI") + if (commandApiPlugin == null) { + Log.w("CommandAPI not found. Mimic commands won't be registered.") + Log.w("Consider installing CommandAPI: https://docs.commandapi.dev/") + registerFallbackCommand(plugin) + return + } else if (!commandApiPlugin.isEnabled) { + Log.w("CommandAPI loaded, but not enabled. Mimic commands won't be registered.") + registerFallbackCommand(plugin) + return + } + + registered = true + registerCommand( + mimic = mimic, + config = config, + pluginFullName = plugin.pluginMeta.displayName, + ) + } + + private fun registerFallbackCommand(plugin: JavaPlugin) { + plugin.server.commandMap.register( + plugin.name.lowercase(), + FallbackCommand(plugin.pluginMeta.displayName), + ) + } + + fun unregister() { + if (registered) CommandAPI.unregister("mimic") + } +} \ No newline at end of file diff --git a/mimic-bukkit/src/main/kotlin/command/commandUtils.kt b/mimic-bukkit/src/main/kotlin/command/textUserInterface.kt similarity index 56% rename from mimic-bukkit/src/main/kotlin/command/commandUtils.kt rename to mimic-bukkit/src/main/kotlin/command/textUserInterface.kt index 91e03c59..3d752ee3 100644 --- a/mimic-bukkit/src/main/kotlin/command/commandUtils.kt +++ b/mimic-bukkit/src/main/kotlin/command/textUserInterface.kt @@ -18,19 +18,19 @@ */ package ru.endlesscode.mimic.command -import org.bukkit.ChatColor -import org.bukkit.command.CommandSender +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor +import ru.endlesscode.mimic.internal.append +import ru.endlesscode.mimic.internal.appendLine -internal fun CommandSender.send(vararg messages: String) { - for (message in messages) { - sendMessage(message.colored()) - } -} +internal fun successText(text: String) = Component.text(text, NamedTextColor.GOLD) +internal fun errorText(text: String) = Component.text(text, NamedTextColor.RED) -internal fun CommandSender.send(messages: Collection) { - for (message in messages) { - sendMessage(message.colored()) +internal fun TextComponent.Builder.appendStats(vararg stats: Pair) { + for ((key, value) in stats) { + append("$key: ", NamedTextColor.DARK_AQUA) + append(value, NamedTextColor.GRAY) + appendLine() } } - -private fun String.colored(): String = ChatColor.translateAlternateColorCodes('&', this) diff --git a/mimic-bukkit/src/main/kotlin/impl/mimic/SafeBukkitItemsRegistry.kt b/mimic-bukkit/src/main/kotlin/impl/mimic/SafeBukkitItemsRegistry.kt index 6f5918cc..eac91e1f 100644 --- a/mimic-bukkit/src/main/kotlin/impl/mimic/SafeBukkitItemsRegistry.kt +++ b/mimic-bukkit/src/main/kotlin/impl/mimic/SafeBukkitItemsRegistry.kt @@ -71,5 +71,5 @@ private fun logImplementationError(provider: ItemsRegistryProvider, throwable: T Log.w(throwable, "Error in ItemsRegistry '${provider.registry.id}' " + "implemented via ${provider.plugin}. " + - "Please, report it to ${provider.plugin.description.authors.joinToString()}.") + "Please, report it to ${provider.plugin.pluginMeta.authors.joinToString()}.") } diff --git a/mimic-bukkit/src/main/kotlin/impl/mmocore/MmoCoreLevelSystem.kt b/mimic-bukkit/src/main/kotlin/impl/mmocore/MmoCoreLevelSystem.kt index 95b65b00..48c9f439 100644 --- a/mimic-bukkit/src/main/kotlin/impl/mmocore/MmoCoreLevelSystem.kt +++ b/mimic-bukkit/src/main/kotlin/impl/mmocore/MmoCoreLevelSystem.kt @@ -5,7 +5,6 @@ import net.Indyuce.mmocore.experience.EXPSource import org.bukkit.entity.Player import ru.endlesscode.mimic.level.BukkitLevelSystem import ru.endlesscode.mimic.level.ExpLevelConverter -import kotlin.math.roundToInt /** Implementation of LevelSystem using MMOCore. */ public class MmoCoreLevelSystem private constructor( @@ -22,9 +21,9 @@ public class MmoCoreLevelSystem private constructor( } override var exp: Double - get() = playerData.experience.toDouble() + get() = playerData.experience set(value) { - playerData.experience = value.roundToInt() + playerData.experience = value } override val totalExpToNextLevel: Double @@ -39,7 +38,7 @@ public class MmoCoreLevelSystem private constructor( } override fun giveExp(expAmount: Double) { - playerData.giveExperience(expAmount.roundToInt(), EXPSource.OTHER) + playerData.giveExperience(expAmount, EXPSource.OTHER) } private val playerData: PlayerData diff --git a/mimic-bukkit/src/main/kotlin/impl/vanilla/ItemMetaPayload.kt b/mimic-bukkit/src/main/kotlin/impl/vanilla/ItemMetaPayload.kt index a3944b62..a5fac67f 100644 --- a/mimic-bukkit/src/main/kotlin/impl/vanilla/ItemMetaPayload.kt +++ b/mimic-bukkit/src/main/kotlin/impl/vanilla/ItemMetaPayload.kt @@ -17,7 +17,7 @@ * along with BukkitMimic. If not, see . */ -@file:UseSerializers(EnchantmentSerializer::class, ItemFlagsSerializer::class) +@file:UseSerializers(EnchantmentSerializer::class, ItemFlagsSerializer::class, MiniMessageComponentSerializer::class) package ru.endlesscode.mimic.impl.vanilla @@ -26,18 +26,16 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlinx.serialization.hocon.decodeFromConfig +import net.kyori.adventure.text.Component import org.bukkit.enchantments.Enchantment import org.bukkit.inventory.ItemFlag -import ru.endlesscode.mimic.internal.DI -import ru.endlesscode.mimic.internal.EnchantmentSerializer -import ru.endlesscode.mimic.internal.ItemFlagsSerializer -import ru.endlesscode.mimic.internal.Log +import ru.endlesscode.mimic.internal.* /** * Payload to configure item's [ItemMeta][org.bukkit.inventory.meta.ItemMeta]. * - * @property name Item name. You can specify colors using symbol `&`. - * @property lore Item lore. You can specify colors using symbol `&`. + * @property name Item name. Supports MiniMessage formatting. + * @property lore Item lore. Supports MiniMessage formatting. * @property isUnbreakable Is item unbreakable. Affects only items that have durability (like weapons or tools). * @property damage Damage to item durability. Affects only items that have durability (like weapons or tools). * @property customModelData A value used to override item model. @@ -48,8 +46,8 @@ import ru.endlesscode.mimic.internal.Log */ @Serializable public data class ItemMetaPayload( - val name: String? = null, - val lore: List? = null, + val name: Component? = null, + val lore: List? = null, @SerialName("unbreakable") val isUnbreakable: Boolean = false, val damage: Int = 0, diff --git a/mimic-bukkit/src/main/kotlin/impl/vanilla/MinecraftItemsRegistry.kt b/mimic-bukkit/src/main/kotlin/impl/vanilla/MinecraftItemsRegistry.kt index 9b8b8bcf..f5776bb1 100644 --- a/mimic-bukkit/src/main/kotlin/impl/vanilla/MinecraftItemsRegistry.kt +++ b/mimic-bukkit/src/main/kotlin/impl/vanilla/MinecraftItemsRegistry.kt @@ -24,8 +24,6 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.ItemMeta import ru.endlesscode.mimic.internal.Log -import ru.endlesscode.mimic.internal.callCompat -import ru.endlesscode.mimic.internal.colorized import ru.endlesscode.mimic.items.BukkitItemsRegistry /** @@ -37,7 +35,7 @@ public class MinecraftItemsRegistry : BukkitItemsRegistry { override val id: String = ID override val knownIds: List by lazy { - Material.values().asSequence() + Material.entries.asSequence() .filter { it.isItem } .map { it.name.lowercase() } .toList() @@ -68,13 +66,13 @@ public class MinecraftItemsRegistry : BukkitItemsRegistry { private fun ItemMeta.applyPayload(payload: ItemMetaPayload): ItemMeta { // Apply text options - setDisplayName(payload.name?.colorized()) - lore = payload.lore?.colorized() + displayName(payload.name) + lore(payload.lore) // Apply damage and custom model data isUnbreakable = payload.isUnbreakable (this as? Damageable)?.damage = payload.damage - if (payload.customModelData != null) trySetCustomModelData(payload.customModelData) + if (payload.customModelData != null) setCustomModelData(payload.customModelData) // Apply enchants and item flags payload.enchantments.forEach { (enchant, level) -> addEnchant(enchant, level, true) } @@ -83,14 +81,6 @@ public class MinecraftItemsRegistry : BukkitItemsRegistry { return this } - private fun ItemMeta.trySetCustomModelData(data: Int) { - callCompat( - "ItemMeta.setCustomModelData", - block = { setCustomModelData(data) }, - compat = { Log.w("Payload field 'custom-model-data' supported only since Minecraft 1.14") }, - ) - } - public companion object { public const val ID: String = "minecraft" } diff --git a/mimic-bukkit/src/main/kotlin/internal/Colors.kt b/mimic-bukkit/src/main/kotlin/internal/Colors.kt index 1b3d0828..a0b75c86 100644 --- a/mimic-bukkit/src/main/kotlin/internal/Colors.kt +++ b/mimic-bukkit/src/main/kotlin/internal/Colors.kt @@ -17,12 +17,11 @@ * along with BukkitMimic. If not, see . */ +// Keep it for now +@file:Suppress("DEPRECATION") + package ru.endlesscode.mimic.internal import org.bukkit.ChatColor -internal fun List.colorized(): List = map { it.colorized() } - -internal fun String.colorized(): String = ChatColor.translateAlternateColorCodes('&', this) - internal fun String.stripColor(): String = checkNotNull(ChatColor.stripColor(this)) diff --git a/mimic-bukkit/src/main/kotlin/internal/Compat.kt b/mimic-bukkit/src/main/kotlin/internal/Compat.kt index c922d683..5df4b7ca 100644 --- a/mimic-bukkit/src/main/kotlin/internal/Compat.kt +++ b/mimic-bukkit/src/main/kotlin/internal/Compat.kt @@ -2,6 +2,7 @@ package ru.endlesscode.mimic.internal private val notSupportedCalls = mutableSetOf() +@Suppress("unused") // Reserved for further compatibility calls internal inline fun callCompat( key: String, block: () -> T, @@ -10,7 +11,7 @@ internal inline fun callCompat( if (key !in notSupportedCalls) { try { return block() - } catch (_: NoSuchMethodError) { + } catch (_: IncompatibleClassChangeError) { notSupportedCalls.add(key) } } diff --git a/mimic-bukkit/src/main/kotlin/internal/Configuration.kt b/mimic-bukkit/src/main/kotlin/internal/Configuration.kt index d9d68e85..95bb8d2b 100644 --- a/mimic-bukkit/src/main/kotlin/internal/Configuration.kt +++ b/mimic-bukkit/src/main/kotlin/internal/Configuration.kt @@ -3,13 +3,8 @@ package ru.endlesscode.mimic.internal import org.bukkit.configuration.Configuration import org.bukkit.configuration.file.FileConfigurationOptions -@Suppress("DEPRECATION") -internal fun FileConfigurationOptions.setHeader(vararg lines: String?) { - callCompat( - "FileConfigurationOptions.setHeader", - block = { setHeader(lines.asList()) }, - compat = { header(lines.joinToString("\n")) }, - ) +internal fun FileConfigurationOptions.setHeader(vararg lines: String) { + setHeader(lines.asList()) } internal fun Configuration.applyDefaults() { @@ -18,9 +13,5 @@ internal fun Configuration.applyDefaults() { } internal fun Configuration.setComments(path: String, vararg comments: String?) { - callCompat( - "Configuration.setComments", - block = { setComments(path, comments.asList()) }, - compat = { /* no-op */ }, - ) + setComments(path, comments.asList()) } diff --git a/mimic-bukkit/src/main/kotlin/internal/Log.kt b/mimic-bukkit/src/main/kotlin/internal/Log.kt index 799ed0ca..ca802406 100644 --- a/mimic-bukkit/src/main/kotlin/internal/Log.kt +++ b/mimic-bukkit/src/main/kotlin/internal/Log.kt @@ -26,13 +26,20 @@ internal object Log { private const val DEBUG_TAG = "[DEBUG]" - private var logger: Logger? = null + private var logger: SimpleLogger? = null private var debug = false /** * Initializes Log with the given logger and specified debug mode. */ fun init(logger: Logger, debug: Boolean = false) { + init(logger::log, debug) + } + + /** + * Initializes Log with the given logger and specified debug mode. + */ + fun init(logger: SimpleLogger, debug: Boolean = false) { this.logger = logger this.debug = debug } @@ -41,14 +48,14 @@ internal object Log { * Write info message to log. */ fun i(message: String) { - logger?.info(message) + logger?.log(Level.INFO, message) } /** * Writes warning messages to log. */ fun w(message: String) { - logger?.warning(message) + logger?.log(Level.WARNING, message) } /** @@ -65,7 +72,7 @@ internal object Log { */ fun d(message: String) { if (debug) { - logger?.info("$DEBUG_TAG $message") + logger?.log(Level.INFO, "$DEBUG_TAG $message") } } @@ -88,7 +95,12 @@ internal object Log { if (debug) { logger?.log(Level.FINE, "$DEBUG_TAG Yay! Long-awaited exception!", throwable) } else if (!quiet) { - logger?.warning("Error occurred. Enable debug mode to see it.") + logger?.log(Level.WARNING, "Error occurred. Enable debug mode to see it.") } } } + +internal fun interface SimpleLogger { + fun log(level: Level, message: String) = log(level, message, null) + fun log(level: Level, message: String?, throwable: Throwable?) +} diff --git a/mimic-bukkit/src/main/kotlin/internal/NamespacedKey.kt b/mimic-bukkit/src/main/kotlin/internal/NamespacedKey.kt index 094828e2..3eb6b5cb 100644 --- a/mimic-bukkit/src/main/kotlin/internal/NamespacedKey.kt +++ b/mimic-bukkit/src/main/kotlin/internal/NamespacedKey.kt @@ -2,20 +2,4 @@ package ru.endlesscode.mimic.internal import org.bukkit.NamespacedKey -internal fun namespacedKeyOf(key: String): NamespacedKey? { - return callCompat( - "NamespacedKey.fromString", - block = { NamespacedKey.fromString(key) }, - compat = { namespacedKeyCompat(key) }, - ) -} - -@Suppress("DEPRECATION") -private fun namespacedKeyCompat(string: String): NamespacedKey? { - val components = string.split(":", limit = 3) - return when (components.size) { - 1 -> NamespacedKey.minecraft(components.first()) - 2 -> NamespacedKey(components.first(), components.last()) - else -> null - } -} +internal fun namespacedKeyOf(key: String): NamespacedKey? = NamespacedKey.fromString(key) diff --git a/mimic-bukkit/src/main/kotlin/internal/Serializers.kt b/mimic-bukkit/src/main/kotlin/internal/Serializers.kt index 98d107e1..922ea667 100644 --- a/mimic-bukkit/src/main/kotlin/internal/Serializers.kt +++ b/mimic-bukkit/src/main/kotlin/internal/Serializers.kt @@ -26,33 +26,62 @@ 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.Component +import net.kyori.adventure.text.minimessage.MiniMessage +import org.bukkit.Registry import org.bukkit.enchantments.Enchantment import org.bukkit.inventory.ItemFlag internal object EnchantmentSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Enchantment", PrimitiveKind.STRING) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( + "org.bukkit.enchantments.Enchantment", + PrimitiveKind.STRING + ) override fun deserialize(decoder: Decoder): Enchantment { val value = decoder.decodeString() val key = namespacedKeyOf(value.replace(" ", "_").lowercase()) - ?: throw SerializationException("$value is not a valid key for enchantment, " + - "only latin letters, digits and symbol _ are allowed") - return Enchantment.getByKey(key) - ?: throw SerializationException("$value is not a valid key for enchantment, " + - "must be one of: [${Enchantment.values().joinToString { it.key.toString() }}]") + ?: throw SerializationException( + "$value is not a valid key for enchantment, only latin letters, digits and symbol _ are allowed" + ) + return Registry.ENCHANTMENT[key] + ?: throw SerializationException( + "$value is not a valid key for enchantment, " + + "must be one of: [${Registry.ENCHANTMENT.joinToString { it.key.toString() }}]" + ) } override fun serialize(encoder: Encoder, value: Enchantment) = encoder.encodeString(value.key.toString()) } internal object ItemFlagsSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ItemFlag", PrimitiveKind.STRING) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( + "org.bukkit.inventory.ItemFlag", + PrimitiveKind.STRING, + ) override fun deserialize(decoder: Decoder): ItemFlag { val value = decoder.decodeString() return enumValueOrNull(value.uppercase()) - ?: throw SerializationException("$value is not a valid ItemFlag, must be one of ${ItemFlag.values()}") + ?: throw SerializationException("$value is not a valid ItemFlag, must be one of ${ItemFlag.entries}") } override fun serialize(encoder: Encoder, value: ItemFlag) = encoder.encodeString(value.name) } + +internal object MiniMessageComponentSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( + "net.kyori.adventure.text.Component", + PrimitiveKind.STRING, + ) + + private val mm = MiniMessage.miniMessage() + + override fun deserialize(decoder: Decoder): Component { + return mm.deserialize(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: Component) { + encoder.encodeString(mm.serialize(value)) + } +} diff --git a/mimic-bukkit/src/main/kotlin/internal/TextComponent.kt b/mimic-bukkit/src/main/kotlin/internal/TextComponent.kt index 519bb223..fe02c903 100644 --- a/mimic-bukkit/src/main/kotlin/internal/TextComponent.kt +++ b/mimic-bukkit/src/main/kotlin/internal/TextComponent.kt @@ -7,7 +7,9 @@ import net.kyori.adventure.text.event.HoverEvent import net.kyori.adventure.text.format.TextColor import net.kyori.adventure.text.format.TextDecoration -internal fun buildTextComponent(builder: TextComponent.Builder.() -> Unit): TextComponent = Component.text(builder) +// See: https://github.com/KyoriPowered/adventure/tree/main/4/extra-kotlin + +internal fun text(builder: TextComponent.Builder.() -> Unit): TextComponent = Component.text(builder) internal fun TextComponent.Builder.appendLine( text: String, @@ -15,7 +17,7 @@ internal fun TextComponent.Builder.appendLine( vararg decorations: TextDecoration, ): TextComponent.Builder = append(text, color, *decorations).appendLine() -internal fun TextComponent.Builder.appendLine(): TextComponent.Builder = append("\n") +internal fun TextComponent.Builder.appendLine(): TextComponent.Builder = append(Component.newline()) internal fun TextComponent.Builder.append( text: String, diff --git a/mimic-bukkit/src/main/kotlin/items/WrappedItemsRegistry.kt b/mimic-bukkit/src/main/kotlin/items/WrappedItemsRegistry.kt index 26eab224..b707ab67 100644 --- a/mimic-bukkit/src/main/kotlin/items/WrappedItemsRegistry.kt +++ b/mimic-bukkit/src/main/kotlin/items/WrappedItemsRegistry.kt @@ -7,7 +7,7 @@ import ru.endlesscode.mimic.WrappedMimicService import ru.endlesscode.mimic.config.MimicConfig internal class WrappedItemsRegistry( - private val delegate: BukkitItemsRegistry, + internal val delegate: BukkitItemsRegistry, private val config: MimicConfig, pluginName: String, pluginManager: PluginManager, @@ -27,3 +27,5 @@ internal class WrappedItemsRegistry( override fun getItem(itemId: String, payload: Any?, amount: Int) = delegate.getItem(itemId, payload, amount) } + +internal fun BukkitItemsRegistry.unwrap() = if (this is WrappedItemsRegistry) delegate else this diff --git a/mimic-bukkit/src/main/resources/plugin.yml b/mimic-bukkit/src/main/resources/plugin.yml deleted file mode 100644 index fe49753f..00000000 --- a/mimic-bukkit/src/main/resources/plugin.yml +++ /dev/null @@ -1,8 +0,0 @@ -loadbefore: - - SkillAPI - - BattleLevels - - CustomItems - - MMOCore - - MMOItems - - Heroes - - QuantumRPG diff --git a/mimic-bukkit/src/test/kotlin/BukkitTestBase.kt b/mimic-bukkit/src/test/kotlin/BukkitTestBase.kt index 393e26ea..7e81196e 100644 --- a/mimic-bukkit/src/test/kotlin/BukkitTestBase.kt +++ b/mimic-bukkit/src/test/kotlin/BukkitTestBase.kt @@ -21,41 +21,59 @@ package ru.endlesscode.mimic import io.mockk.every import io.mockk.mockk -import io.mockk.mockkStatic import org.bukkit.Bukkit import org.bukkit.Server import org.bukkit.entity.Player import org.bukkit.plugin.Plugin import org.bukkit.plugin.ServicesManager import org.bukkit.plugin.SimpleServicesManager +import ru.endlesscode.mimic.internal.Log import java.util.* /** Base for all Bukkit-related tests. */ +@Suppress("UnstableApiUsage") open class BukkitTestBase { - protected val server: Server = mockServer() protected val plugin: Plugin = mockPlugin(server) protected val player: Player = mockPlayer() protected val servicesManager: ServicesManager = server.servicesManager init { - mockBukkit() - } + Log.init({ level, message, throwable -> + println("$level: $message") + throwable?.printStackTrace() + }) - private fun mockServer(): Server = mockk { - every { pluginManager } returns mockk(relaxUnitFun = true) - every { servicesManager } returns SimpleServicesManager() + mockBukkit() } private fun mockPlugin(mockServer: Server): Plugin = mockk { every { server } returns mockServer + every { pluginMeta } returns mockk { + every { authors } returns listOf("Plugin Author") + } } private fun mockPlayer(): Player = mockk(relaxUnitFun = true) { every { uniqueId } returns UUID.randomUUID() } - private fun mockBukkit() { - mockkStatic(Bukkit::class) - every { Bukkit.getServer() } returns server + private companion object { + val server: Server = mockServer() + + private fun mockServer(): Server = mockk { + every { name } returns "MockServer" + every { version } returns "0.0.0" + every { bukkitVersion } returns "0.0.0" + + every { pluginManager } returns mockk(relaxUnitFun = true) + every { servicesManager } returns SimpleServicesManager() + every { isPrimaryThread } returns true + every { logger } returns mockk(relaxUnitFun = true) + } + + fun mockBukkit() { + @Suppress("SENSELESS_COMPARISON") // The annotation lies about nullability + if (Bukkit.getServer() == null) Bukkit.setServer(server) + } } } diff --git a/mimic-bukkit/src/test/kotlin/impl/mimic/MimicItemsRegistryTest.kt b/mimic-bukkit/src/test/kotlin/impl/mimic/MimicItemsRegistryTest.kt index 9c13c259..7e686858 100644 --- a/mimic-bukkit/src/test/kotlin/impl/mimic/MimicItemsRegistryTest.kt +++ b/mimic-bukkit/src/test/kotlin/impl/mimic/MimicItemsRegistryTest.kt @@ -32,7 +32,6 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.junit.jupiter.params.provider.ValueSource import ru.endlesscode.mimic.BukkitTestBase -import ru.endlesscode.mimic.impl.vanilla.MinecraftItemsRegistry import ru.endlesscode.mimic.items.BukkitItemsRegistry import kotlin.test.Test @@ -42,19 +41,19 @@ internal class MimicItemsRegistryTest : BukkitTestBase() { private val itemsService: BukkitItemsRegistry = MimicItemsRegistry(servicesManager) init { - servicesManager.register(BukkitItemsRegistry::class.java, MinecraftItemsRegistry(), plugin, Lowest) + servicesManager.register(BukkitItemsRegistry::class.java, TestItemRegistry(), plugin, Lowest) servicesManager.register(BukkitItemsRegistry::class.java, itemsService, plugin, Highest) } @ParameterizedTest - @ValueSource(strings = ["acacia_boat", "minecraft:acacia_boat"]) + @ValueSource(strings = ["acacia_boat", "test:acacia_boat"]) fun `when check is same item - should return true`(itemId: String) { val item = ItemStack(Material.ACACIA_BOAT) itemsService.isSameItem(item, itemId).shouldBeTrue() } @ParameterizedTest - @ValueSource(strings = ["acacia_boat", "minecraft:acacia_boat"]) + @ValueSource(strings = ["acacia_boat", "test:acacia_boat"]) fun `when get item - should return item stack`(itemId: String) { val item = itemsService.getItem(itemId).shouldNotBeNull() @@ -65,7 +64,7 @@ internal class MimicItemsRegistryTest : BukkitTestBase() { } @ParameterizedTest - @ValueSource(strings = ["ns:acacia_boat", "minecraft:unknown", "42"]) + @ValueSource(strings = ["ns:acacia_boat", "test:unknown", "42"]) fun `when get unknown item - should return null`(itemId: String) { itemsService.getItem(itemId).shouldBeNull() } @@ -73,14 +72,14 @@ internal class MimicItemsRegistryTest : BukkitTestBase() { @Test fun `when get id - should return id`() { val item = ItemStack(Material.ACACIA_BOAT) - itemsService.getItemId(item) shouldBe "minecraft:acacia_boat" + itemsService.getItemId(item) shouldBe "test:acacia_boat" } @ParameterizedTest @CsvSource( "air, true", "ns:air, false", - "minecraft:air, true", + "test:air, true", "unknown, false", "gold_sword, false", "golden_sword, true", @@ -89,3 +88,17 @@ internal class MimicItemsRegistryTest : BukkitTestBase() { itemsService.isItemExists(itemId) shouldBe shouldExist } } + +private class TestItemRegistry : BukkitItemsRegistry { + override val id = "test" + override val knownIds = Material.entries.map { it.name.lowercase() } + + override fun isItemExists(itemId: String): Boolean = itemId in knownIds + + override fun getItemId(item: ItemStack): String = item.type.name.lowercase() + + override fun getItem(itemId: String, payload: Any?, amount: Int): ItemStack { + val material = Material.valueOf(itemId.uppercase()) + return ItemStack(material, amount) + } +} diff --git a/mimic-bukkit/src/test/kotlin/impl/vanilla/ItemMetaPayloadTest.kt b/mimic-bukkit/src/test/kotlin/impl/vanilla/ItemMetaPayloadTest.kt index 7ce060cc..aabfeb9a 100644 --- a/mimic-bukkit/src/test/kotlin/impl/vanilla/ItemMetaPayloadTest.kt +++ b/mimic-bukkit/src/test/kotlin/impl/vanilla/ItemMetaPayloadTest.kt @@ -21,6 +21,7 @@ package ru.endlesscode.mimic.impl.vanilla import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe +import net.kyori.adventure.text.Component.text import org.bukkit.inventory.ItemFlag import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -55,8 +56,8 @@ internal class ItemMetaPayloadTest { @JvmStatic fun validData(): Stream = Stream.of( // Simple cases - arguments("{name: Name}", ItemMetaPayload(name = "Name")), - arguments("name=Name", ItemMetaPayload(name = "Name")), + arguments("{name: Name}", ItemMetaPayload(name = text("Name"))), + arguments("name=Name", ItemMetaPayload(name = text("Name"))), arguments( """ name = Name, @@ -67,8 +68,8 @@ internal class ItemMetaPayloadTest { flags = [HIDE_ATTRIBUTES, HIDE_DYE] """, ItemMetaPayload( - name = "Name", - lore = listOf("Line1", "Line2"), + name = text("Name"), + lore = listOf(text("Line1"), text("Line2")), isUnbreakable = true, damage = 42, customModelData = 24, diff --git a/mimic-bukkit/src/test/kotlin/impl/vanilla/MinecraftItemsRegistryTest.kt b/mimic-bukkit/src/test/kotlin/impl/vanilla/MinecraftItemsRegistryTest.kt deleted file mode 100644 index cb7c4b82..00000000 --- a/mimic-bukkit/src/test/kotlin/impl/vanilla/MinecraftItemsRegistryTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of BukkitMimic. - * Copyright (C) 2020 Osip Fatkullin - * Copyright (C) 2020 EndlessCode Group and contributors - * - * BukkitMimic is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * BukkitMimic is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with BukkitMimic. If not, see . - */ - -package ru.endlesscode.mimic.impl.vanilla - -import io.kotest.assertions.assertSoftly -import io.kotest.matchers.nulls.shouldBeNull -import io.kotest.matchers.nulls.shouldNotBeNull -import io.kotest.matchers.shouldBe -import org.bukkit.Material -import org.bukkit.inventory.ItemStack -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import ru.endlesscode.mimic.items.BukkitItemsRegistry -import kotlin.test.Test - -class MinecraftItemsRegistryTest { - - // SUT - private val itemsService: BukkitItemsRegistry = MinecraftItemsRegistry() - - @Test - fun `when get item - should return item stack`() { - val item = itemsService.getItem("acacia_boat").shouldNotBeNull() - - assertSoftly { - item.type shouldBe Material.ACACIA_BOAT - item.amount shouldBe 1 - } - } - - @Test - fun `when get unknown item - should return null`() { - itemsService.getItem("super_duper_item").shouldBeNull() - } - - @ParameterizedTest - @CsvSource( - "cobblestone, 0, 1", - "cobblestone, 32, 32", - "cobblestone, 65, 64", - "acacia_boat, 2, 1" - ) - fun `when get item with amount - should return item stack with right amount`( - itemId: String, - amount: Int, - realAmount: Int - ) { - itemsService.getItem(itemId, amount) - .shouldNotBeNull() - .amount shouldBe realAmount - } - - @Test - fun `when get id - should return id`() { - val item = ItemStack(Material.ACACIA_BOAT) - itemsService.getItemId(item) shouldBe "acacia_boat" - } - - @ParameterizedTest - @CsvSource( - "air, true", - "unknown, false", - "gold_sword, false", - "golden_sword, true" - ) - fun `when check is item exists`(itemId: String, shouldExist: Boolean) { - itemsService.isItemExists(itemId) shouldBe shouldExist - } -}