diff --git a/.editorconfig b/.editorconfig index bfe6134..c764bbc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,49 +21,78 @@ ij_formatter_tags_enabled = true ij_formatter_on_tag = @formatter:on ij_formatter_off_tag = @formatter:off -[{*.kt, *.kts}] -ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL -ij_continuation_indent_size = 4 # To match ktlint settings -ij_kotlin_keep_indents_on_empty_lines = false +[{*.kt,*.kts}] +# Tabs and Indents +# continuation_indent_size = 4 to match ktlint settings +ij_kotlin_continuation_indent_size = 4 +ij_kotlin_keep_indents_on_empty_lines = unset -## Wrapping and Braces -# Keep when reformatting +# Spaces +## Before parentheses +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_when_parentheses = true +## Around operators +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_range = false +## Other +ij_kotlin_space_before_comma = false +ij_kotlin_space_after_comma = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_space_before_lambda_arrow = true + +# Wrapping and Braces +## Keep when reformatting ij_kotlin_keep_line_breaks = true ij_kotlin_keep_first_column_comment = true -# Extends/implements list +## Extends/implements list ij_kotlin_extends_list_wrap = normal ij_kotlin_align_multiline_extends_list = false ij_kotlin_continuation_indent_in_supertype_lists = false -# Function declaration parameters +## Function declaration parameters ij_kotlin_method_parameters_wrap = on_every_item ij_kotlin_align_multiline_parameters = true ij_kotlin_method_parameters_new_line_after_left_paren = true ij_kotlin_method_parameters_right_paren_on_new_line = true ij_kotlin_continuation_indent_in_parameter_lists = false -# Function call arguments +## Function call arguments ij_kotlin_call_parameters_wrap = on_every_item ij_kotlin_align_multiline_parameters_in_calls = false ij_kotlin_call_parameters_new_line_after_left_paren = true ij_kotlin_call_parameters_right_paren_on_new_line = true ij_kotlin_continuation_indent_in_argument_lists = false -# Function parentheses +## Function parentheses ij_kotlin_align_multiline_method_parentheses = false -# Chained function calls +## Chained function calls ij_kotlin_method_call_chain_wrap = normal ij_kotlin_wrap_first_method_in_call_chain = false ij_kotlin_continuation_indent_for_chained_calls = false -# 'if()' statement +## 'if()' statement ij_kotlin_else_on_new_line = false ij_kotlin_if_rparen_on_new_line = true ij_kotlin_continuation_indent_in_if_conditions = false -# 'do ... while()' statement +## 'do ... while()' statement ij_kotlin_while_on_new_line = false -# 'try' statement +## 'try' statement ij_kotlin_catch_on_new_line = false ij_kotlin_finally_on_new_line = false -# Binary expressions +## Binary expressions ij_kotlin_align_multiline_binary_operation = false -# Wraps +## Wraps ij_kotlin_assignment_wrap = normal ij_kotlin_enum_constants_wrap = off ij_kotlin_class_annotation_wrap = split_into_lines @@ -71,81 +100,62 @@ ij_kotlin_method_annotation_wrap = split_into_lines ij_kotlin_field_annotation_wrap = split_into_lines ij_kotlin_parameter_annotation_wrap = off ij_kotlin_variable_annotation_wrap = off -# 'when' statements +## 'when' statements ij_kotlin_align_in_columns_case_branch = false -# Braces placement +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_indent_before_arrow_on_new_line = true +## Braces placement ij_kotlin_lbrace_on_next_line = false -# Expression body functions +## Expression body functions ij_kotlin_wrap_expression_body_functions = 1 ij_kotlin_continuation_indent_for_expression_bodies = false -# Elvis expressions +## Elvis expressions ij_kotlin_wrap_elvis_expressions = 1 ij_kotlin_continuation_indent_in_elvis = false -## Spaces -# Before Parentheses -ij_kotlin_space_before_if_parentheses = true -ij_kotlin_space_before_for_parentheses = true -ij_kotlin_space_before_while_parentheses = true -ij_kotlin_space_before_catch_parentheses = true -ij_kotlin_space_before_when_parentheses = true -# Around Operators -ij_kotlin_spaces_around_assignment_operators = true -ij_kotlin_spaces_around_logical_operators = true -ij_kotlin_spaces_around_equality_operators = true -ij_kotlin_spaces_around_relational_operators = true -ij_kotlin_spaces_around_additive_operators = true -ij_kotlin_spaces_around_multiplicative_operators = true -ij_kotlin_spaces_around_unary_operator = false -ij_kotlin_spaces_around_range = false -# Other -ij_kotlin_space_before_comma = false -ij_kotlin_space_after_comma = true -ij_kotlin_space_before_type_colon = false -ij_kotlin_space_after_type_colon = true -ij_kotlin_space_after_extend_colon = true -ij_kotlin_space_before_extend_colon = true -ij_kotlin_insert_whitespaces_in_simple_one_line_method = true -ij_kotlin_spaces_around_function_type_arrow = true -ij_kotlin_spaces_around_when_arrow = true -ij_kotlin_space_before_lambda_arrow = true - -## Blank Lines -# Keep Maximum Blank Lines +# Blank Lines +## Keep maximum blank lines ij_kotlin_keep_blank_lines_in_declarations = 1 ij_kotlin_keep_blank_lines_in_code = 1 ij_kotlin_keep_blank_lines_before_right_brace = 0 -# Minimum Blank Lines +## Minimum blank lines ij_kotlin_blank_lines_after_class_header = 0 ij_kotlin_blank_lines_around_block_when_branches = 1 ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 -## Imports +# Imports ij_kotlin_name_count_to_use_star_import = 5 ij_kotlin_name_count_to_use_star_import_for_members = 3 ij_kotlin_import_nested_classes = false -ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** -ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** +ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ -## Other +# Other +## Trailing comma ij_kotlin_allow_trailing_comma = true -ij_kotlin_allow_trailing_comma_on_call_site = true +ij_kotlin_allow_trailing_comma_on_call_site = false -## Code generation +# Code generation +## Comment code ij_kotlin_line_comment_at_first_column = true ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false ij_kotlin_block_comment_at_first_column = true +ij_kotlin_block_comment_add_space = false + +# Load/Save +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL [*.md] trim_trailing_whitespace = false -[{*.yaml, *.yml}] +[{*.yaml,*.yml}] indent_size = 2 -ij_yaml_spaces_within_brackets = false -ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_indents_on_empty_lines = unset ij_yaml_keep_line_breaks = true +ij_yaml_spaces_within_brackets = false -[{*.bash, *.sh, *.zsh}] +[{*.bash,*.sh,*.zsh}] indent_size = 2 tab_width = 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index a8cacdf..4276497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,16 @@ ### Changed +- **Breaking change!** + Refactor development server implementation to use [jpenilla/run-task](https://github.com/jpenilla/run-task/) plugin and integrate run-paper for server execution, + improving maintainability and compatibility with various server versions. - Set the default [JVM toolchain](https://docs.gradle.org/current/userguide/toolchains.html) version instead of setting JVM target and source compatibility to 1.8. - The default JVM version depends on [Paper requirements](https://docs.papermc.io/paper/getting-started#requirements). + By default, the minimal supported JVM version compatible with the specified `bukkit.server.version` is used. - Use lazy API for `bukkit.apiVersion` and `bukkit.generateMeta` properties. +- Accept EULA using CLI parameter `-Dcom.mojang.eula.agree=true` instead of changing `eula.txt` +- Change the default value of `bukkit.server.debug` to `false`. + It is recommended to use IDE facilities to run server with enabled debugging. ### Fixed diff --git a/README.md b/README.md index 36163c7..5724c39 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ BukkitGradle [![Version](https://img.shields.io/github/release/EndlessCodeGroup/BukkitGradle/all.svg?style=flat-square)](https://plugins.gradle.org/plugin/ru.endlesscode.bukkitgradle) -[![Build Status](https://img.shields.io/travis/EndlessCodeGroup/BukkitGradle.svg?style=flat-square)](https://travis-ci.org/EndlessCodeGroup/BukkitGradle) [![license](https://img.shields.io/github/license/EndlessCodeGroup/BukkitGradle.svg?style=flat-square)](https://github.com/EndlessCodeGroup/BukkitGradle/blob/master/LICENSE) + ============ -Gradle utilities for easier writing Bukkit plugins. +Gradle utilities to simplify Bukkit/Spigot plugins writing and debugging. > [!WARNING] > This plugin is not being maintained anymore, @@ -33,16 +33,15 @@ Gradle utilities for easier writing Bukkit plugins. - Provides short extension functions to add common repositories and dependencies - Generates plugin.yml from Gradle project information - Allows running dev server from IDE -- Supports two cores for dev server: Spigot and Paper -- Automatically downloads and updates BuildTools or Paperclip -- Automatically copies your plugin to plugins dir on server running +- Runs server using [jpenilla/run-task] #### TODO: - Add smart dependency system ## Installation + [BukkitGradle on plugins.gradle.org](https://plugins.gradle.org/plugin/ru.endlesscode.bukkitgradle) -> **Note:** Gradle 6.6+ required +> **Note:** Gradle 8.0+ is required #### With new plugins mechanism ```kotlin @@ -197,62 +196,75 @@ Some dependencies also add a repository needed for them. If you need more extension-functions, [create issue][issue]. ## Running Dev server -Before running server you should configure dev server location. -You can define it in `local.properties` file (that was automatically created in project directory on refresh): -```properties -# Absolute path to dev server -server.dir=/path/to/buildtools/ +This plugin pre-configures [jpenilla/run-task] according to the specified [configuration](#dev-server-configuration). +Use `:runServer` task to run the dev server: + +```bash +./gradlew runServer ``` -If you use Spigot (see `bukkit.server.core`) you also should specify BuildTools location. For Paper no additional actions -needed. +> [!TIP] +> It is possible to create a run configuration for IDEA by running `:buildIdeaRun` task. +> The configuration will be stored in `/.run` directory so it can be shared through VCS. +> The directory can be changed by configuring the `:buildIdeaRun` task: +> ```kotlin +> tasks.buildIdeaRun { +> configurationsDir = file(".idea/runConfigurations") +> } +> ``` + +By default, the server will be located at `/run` but you can change it by providing Gradle property `bukkitgradle.server.dir`: + ```properties -# Absolute path to directory that contains BuildTools.jar -buildtools.dir=/path/to/buildtools/ +# gradle.properties +bukkitgradle.server.dir=build/run ``` -If there no BuildTools.jar it will be automatically downloaded. - -> **Tip:** you can define it globally, for all projects that uses BukkitGradle. -> Specify environment variables `BUKKIT_DEV_SERVER_HOME` -and `BUKKIT_BUILDTOOLS_HOME`. -#### On IntelliJ IDEA -Run `:buildIdeaRun` task. -Run Configuration will be added to your IDE. -It will be automatically refreshed when you change server configurations. +Alternatively, you can configure `runServer` task: -![Run Configuration](http://image.prntscr.com/image/1a12a03b8ac54fccb7d5b70a335fa996.png) +```kotlin +tasks.runServer { + runDirectory.set(file("build/run")) +} +``` -#### On other IDEs -Run `:runServer` task. +> [!TIP] +> It is possible to configure server directory shared between multiple projects. +> Set the `bukkitgradle.server.dir` property in `$HOME/.gradle/gradle.properties`. +> +> This file contains local configurations to be used for all Gradle projects. +> The value specified in project's `gradle.properties` takes precedence over the global one. ### Dev server configuration -To accept EULA and change settings use `bukkit.server` section: + +Use `bukkit.server` section to accept EULA and configure the server: + ```groovy bukkit { - // INFO: Here used default values + // INFO: Default values are used here server { - // Core type. It can be 'spigot' or 'paper' - core = "spigot" // Server version - version = "1.16.4" // If not specified, apiVersion will be used + version = "1.16.4" // If not specified, bukkit.apiVersion will be used // Accept EULA eula = false // Set online-mode flag onlineMode = false - // Debug mode (listen 5005 port, if you use running from IDEA this option will be ignored) + // Debug mode (listen to 5005 port) debug = true - // Set server encoding (flag -Dfile.encoding) + // Set default file encoding (flag -Dfile.encoding) encoding = "UTF-8" // JVM arguments javaArgs("-Xmx1G") // Bukkit arguments - bukkitArgs("nogui") + bukkitArgs() } } ``` -EULA and online-mode settings in `build.gradle` always rewrites settings in `eula.txt` and `server.properties` + +> [!NOTE] +> `eula` and `online-mode` options specified in `bukkit.server` always take precedence over the values specified +> in `eula.txt` and `server.properties` ## Migration Guide @@ -321,4 +333,5 @@ If there are any problems, [create an issue][issue]. [MIT](LICENSE) (c) 2020 EndlessCode Group +[jpenilla/run-task]: https://github.com/jpenilla/run-task/ [issue]: https://github.com/EndlessCodeGroup/BukkitGradle/issues/new diff --git a/build.gradle.kts b/build.gradle.kts index 56c32ca..af984b1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,9 +24,11 @@ tasks.test { repositories { mavenCentral() + gradlePluginPortal() } dependencies { + implementation("xyz.jpenilla.run-paper:xyz.jpenilla.run-paper.gradle.plugin:2.3.1") implementation("de.undercouch:gradle-download-task:5.6.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") implementation("com.charleskorn.kaml:kaml:0.74.0") @@ -50,7 +52,7 @@ publishing { gradlePlugin { website = "https://github.com/EndlessCodeGroup/BukkitGradle" - vcsUrl = website + vcsUrl = "$website.git" plugins { create("bukkitGradle") { diff --git a/gradle.properties b/gradle.properties index ebc5141..8c0782b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group=ru.endlesscode description=Bukkit Gradle integration plugins -version=0.10.1 +version=0.11.0-SNAPSHOT org.gradle.jvmargs=-Xmx3G org.gradle.parallel=true org.gradle.daemon=true diff --git a/src/main/kotlin/BukkitGradlePlugin.kt b/src/main/kotlin/BukkitGradlePlugin.kt index 85aed7a..19b39c3 100644 --- a/src/main/kotlin/BukkitGradlePlugin.kt +++ b/src/main/kotlin/BukkitGradlePlugin.kt @@ -4,7 +4,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.dependencies.Dependencies import ru.endlesscode.bukkitgradle.meta.PluginMetaPlugin @@ -12,6 +11,7 @@ import ru.endlesscode.bukkitgradle.meta.extension.PluginMetaImpl import ru.endlesscode.bukkitgradle.meta.util.MinecraftVersion import ru.endlesscode.bukkitgradle.meta.util.StringUtils import ru.endlesscode.bukkitgradle.meta.util.parsedApiVersion +import ru.endlesscode.bukkitgradle.meta.util.resolveMinimalJavaVersion import ru.endlesscode.bukkitgradle.server.DevServerPlugin import ru.endlesscode.bukkitgradle.server.extension.ServerConfigurationImpl @@ -40,7 +40,7 @@ public class BukkitGradlePlugin : Plugin { extensions.configure { toolchain { - languageVersion.convention(bukkit.parsedApiVersion.map(::resolveRecommendedJavaVersion)) + languageVersion.convention(bukkit.parsedApiVersion.map(::resolveMinimalJavaVersion)) } } } @@ -77,12 +77,4 @@ public class BukkitGradlePlugin : Plugin { version < MinecraftVersion.V1_20_5 -> version.withoutPatch().toString() else -> version.toString() } - - // See: https://docs.papermc.io/paper/getting-started#requirements - private fun resolveRecommendedJavaVersion(version: MinecraftVersion): JavaLanguageVersion = when { - version >= MinecraftVersion.V1_17_1 -> JavaLanguageVersion.of(21) - version >= MinecraftVersion.V1_16_5 -> JavaLanguageVersion.of(16) - version >= MinecraftVersion.V1_12_0 -> JavaLanguageVersion.of(11) - else -> JavaLanguageVersion.of(8) - } } diff --git a/src/main/kotlin/extensions/Provider.kt b/src/main/kotlin/extensions/Provider.kt index 0a3a933..1ff73d5 100644 --- a/src/main/kotlin/extensions/Provider.kt +++ b/src/main/kotlin/extensions/Provider.kt @@ -1,5 +1,11 @@ package ru.endlesscode.bukkitgradle.extensions +import org.gradle.api.Project +import org.gradle.api.file.Directory import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import java.io.File internal fun Property.finalizedOnRead(): Property = apply { finalizeValueOnRead() } + +internal fun Project.directoryProvider(file: () -> File?): Provider = layout.dir(provider(file)) diff --git a/src/main/kotlin/meta/util/MinecraftVersion.kt b/src/main/kotlin/meta/util/MinecraftVersion.kt index e2cb947..3f63af8 100644 --- a/src/main/kotlin/meta/util/MinecraftVersion.kt +++ b/src/main/kotlin/meta/util/MinecraftVersion.kt @@ -1,5 +1,6 @@ package ru.endlesscode.bukkitgradle.meta.util +import org.gradle.jvm.toolchain.JavaLanguageVersion import ru.endlesscode.bukkitgradle.Bukkit @JvmInline @@ -21,13 +22,23 @@ internal value class MinecraftVersion(private val value: Int) : Comparable= MinecraftVersion.V1_20_5 -> JavaLanguageVersion.of(21) + // https://minecraft.wiki/w/Java_Edition_1.18#General_2 + version >= MinecraftVersion.V1_18_0 -> JavaLanguageVersion.of(17) + // https://minecraft.wiki/w/Java_Edition_1.17#General_2 + version >= MinecraftVersion.V1_17_0 -> JavaLanguageVersion.of(16) + else -> JavaLanguageVersion.of(8) +} diff --git a/src/main/kotlin/server/Constants.kt b/src/main/kotlin/server/Constants.kt index 7e546a7..e3a8fef 100644 --- a/src/main/kotlin/server/Constants.kt +++ b/src/main/kotlin/server/Constants.kt @@ -2,15 +2,4 @@ package ru.endlesscode.bukkitgradle.server internal object ServerConstants { const val DEFAULT_VERSION: String = "1.16.4" - const val FILE_CORE: String = "core.jar" -} - -internal object BuildToolsConstants { - const val URL: String = - "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" -} - -internal object PaperConstants { - const val URL_PAPER_VERSIONS: String = - "https://gist.githubusercontent.com/osipxd/6119732e30059241c2192c4a8d2218d9/raw/paper-versions.json" } diff --git a/src/main/kotlin/server/DeprecatedServerProperties.kt b/src/main/kotlin/server/DeprecatedServerProperties.kt new file mode 100644 index 0000000..fe6eaa0 --- /dev/null +++ b/src/main/kotlin/server/DeprecatedServerProperties.kt @@ -0,0 +1,77 @@ +package ru.endlesscode.bukkitgradle.server + +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.slf4j.LoggerFactory +import java.io.File +import java.util.* + +internal class DeprecatedServerProperties(rootDir: File, private val providers: ProviderFactory) { + + val devServerDir: File? + + private val logger = LoggerFactory.getLogger("DeprecatedServerProperties") + private val properties = Properties() + + private val propertiesFile = File(rootDir, NAME) + + init { + if (propertiesFile.exists()) { + properties.load(propertiesFile.reader()) + } + + // Read properties eagerly to show deprecation warning + devServerDir = getDir(DEV_SERVER_DIR) + getDir(BUILD_TOOLS_DIR) + } + + private fun getDir(property: Property): File? { + val path = get(property) ?: return null + + showDeprecationWarning(property) + return File(path) + .absoluteFile + .also { it.mkdirs() } + } + + private fun get(property: Property): String? { + val localProp = properties.getProperty(property.name) + val globalEnv = getEnvProvider(property.envVariable) + return localProp ?: globalEnv.orNull + } + + private fun getEnvProvider(name: String): Provider { + return providers.environmentVariable(name) + } + + private fun showDeprecationWarning(property: Property) { + logger.warn( + """ + Property '${property.name}' is Deprecated. + ${property.replacementNote} + Please, remove the property from $NAME and unset '${property.envVariable}' environment variable. + """.trimIndent() + ) + } + + private data class Property( + val name: String, + val envVariable: String, + val replacementNote: String, + ) + + private companion object { + private const val NAME: String = "local.properties" + + private val BUILD_TOOLS_DIR: Property = Property( + name = "buildtools.dir", + envVariable = "BUKKIT_BUILDTOOLS_HOME", + replacementNote = "Building and running Spigot server is not supported anymore.", + ) + private val DEV_SERVER_DIR: Property = Property( + name = "server.dir", + envVariable = "BUKKIT_DEV_SERVER_HOME", + replacementNote = "Set Gradle property 'bukkitgradle.server.dir' in \$HOME/.gradle/gradle.properties instead.", + ) + } +} diff --git a/src/main/kotlin/server/DevServerPlugin.kt b/src/main/kotlin/server/DevServerPlugin.kt index 5993e6e..a7a8857 100644 --- a/src/main/kotlin/server/DevServerPlugin.kt +++ b/src/main/kotlin/server/DevServerPlugin.kt @@ -1,23 +1,22 @@ package ru.endlesscode.bukkitgradle.server -import de.undercouch.gradle.tasks.download.Download import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.Directory +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Copy import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.register +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.Bukkit -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT import ru.endlesscode.bukkitgradle.bukkit -import ru.endlesscode.bukkitgradle.meta.extension.PluginMeta -import ru.endlesscode.bukkitgradle.server.extension.CoreType +import ru.endlesscode.bukkitgradle.meta.util.resolveMinimalJavaVersion import ru.endlesscode.bukkitgradle.server.extension.ServerConfiguration -import ru.endlesscode.bukkitgradle.server.task.* +import ru.endlesscode.bukkitgradle.server.task.CreateIdeaGradleRunConfiguration +import ru.endlesscode.bukkitgradle.server.task.PrepareServer +import xyz.jpenilla.runpaper.RunPaperPlugin +import xyz.jpenilla.runpaper.task.RunServer import java.io.File public class DevServerPlugin : Plugin { @@ -28,9 +27,6 @@ public class DevServerPlugin : Plugin { private val serverConfiguration: ServerConfiguration get() = bukkit.server - private val pluginMeta: PluginMeta - get() = bukkit.meta - private val tasks: TaskContainer get() = project.tasks @@ -39,125 +35,73 @@ public class DevServerPlugin : Plugin { project = target bukkit = project.bukkit - val properties = ServerProperties(project.rootDir, project.providers) - val coreVersion = project.provider { serverConfiguration.version }.orElse(bukkit.apiVersion) - val serverDir = project.layout.dir(coreVersion.map { File(properties.devServerDir, it) }) - val buildToolsDir = project.provider { properties.buildToolsDir } - - // Register tasks - val buildServerCore = registerBuildServerCoreTask(buildToolsDir, coreVersion) - val downloadPaperclip = registerDownloadPaperclip(coreVersion) - val copyServerCore = registerCopyServerCoreTask(buildServerCore, downloadPaperclip, serverDir) - - val prepareServer = registerPrepareServerTask(copyServerCore, serverDir) - registerRunServerTask(prepareServer, serverDir) - - registerBuildIdeRunTask(serverDir) - } - - private fun registerBuildServerCoreTask( - buildToolsDir: Provider, - coreVersion: Provider - ): TaskProvider { - val downloadBuildTools = tasks.register("downloadBuildTools") { - group = TASKS_GROUP_BUKKIT - description = "Download BuildTools" - - src(BuildToolsConstants.URL) - dest(buildToolsDir) - onlyIfModified(true) - } - - return tasks.register("buildServerCore") { - buildToolsFile.set(downloadBuildTools.map { it.outputFiles.single() }) - workingDir(buildToolsDir) - version.set(coreVersion) + target.apply() + val configuredServerDir = target.resolveConfiguredServerDir() + + // Preconfigure RunServer task + val serverVersion = project.provider { serverConfiguration.version }.orElse(bukkit.apiVersion) + val runServer = tasks.named("runServer") { + version.convention(serverVersion) + if (configuredServerDir != null) runDirectory.convention(configuredServerDir) + jvmArgs(serverConfiguration.buildJvmArgs()) + args(serverConfiguration.bukkitArgs) + + defaultCharacterEncoding = serverConfiguration.encoding + debugOptions { + enabled.convention(serverConfiguration.debug) + suspend.convention(false) + } } - } - private fun registerDownloadPaperclip(coreVersion: Provider): TaskProvider { - val bukkitGradleDir = project.layout.buildDirectory.file("bukkit-gradle") + // RunPaperPlugin uses afterEvaluate under the hood, so we have to use afterEvaluate + // to set our conventions after theirs + project.afterEvaluate { configureDefaultJvmForServer() } - val downloadPaperVersions = tasks.register("downloadPaperVersions") { - group = TASKS_GROUP_BUKKIT - description = "Download file with paperclip versions" + val prepareServer = registerPrepareServerTask(runServer) + runServer.configure { dependsOn(prepareServer) } - src(PaperConstants.URL_PAPER_VERSIONS) - dest(bukkitGradleDir) - quiet(true) - onlyIfModified(true) - } + registerBuildIdeRunTask(runServer) + } - return tasks.register("downloadPaperclip") { - paperVersionsFile.set(downloadPaperVersions.map { it.outputFiles.single() }) - version.set(coreVersion) - dest(bukkitGradleDir) + private fun Project.configureDefaultJvmForServer() { + val toolchains = project.extensions.findByType() ?: return + val spec = the().toolchain + + tasks.withType().configureEach { + javaLauncher.convention( + toolchains.launcherFor { + languageVersion.convention(version.map(::resolveMinimalJavaVersion)) + implementation.convention(spec.implementation) + vendor.convention(spec.vendor) + } + ) } } - private fun registerCopyServerCoreTask( - buildServerCore: TaskProvider, - downloadPaperclip: TaskProvider, - serverDir: Provider - ): TaskProvider { - return tasks.register("copyServerCore") { - group = TASKS_GROUP_BUKKIT - description = "Copy server core to server directory" - - val source = if (serverConfiguration.coreType == CoreType.SPIGOT) { - buildServerCore.map { it.spigotFile.get() } - } else { - downloadPaperclip.map { it.paperclipFile.get() } - } + private fun Project.resolveConfiguredServerDir(): Provider? { + val deprecated = DeprecatedServerProperties(rootDir, providers) - from(source) - rename { ServerConstants.FILE_CORE } - into(serverDir) - } + val serverDirProperty = providers.gradleProperty("bukkitgradle.server.dir") + .orElse(provider { deprecated.devServerDir?.absolutePath }) + .orNull ?: return null + val serverDirFile = provider { File(serverDirProperty).absoluteFile } + return layout.dir(serverDirFile) } private fun registerPrepareServerTask( - copyServerCore: TaskProvider, - serverDir: Provider + runServer: Provider, ): TaskProvider { - val copyPlugins = tasks.register("copyPlugins") { - group = TASKS_GROUP_BUKKIT - description = "Copy plugins to dev server." - - val jarTaskName = if (project.plugins.hasPlugin("com.gradleup.shadow")) "shadowJar" else "jar" - from(tasks.named(jarTaskName)) - into(serverDir.map { project.mkdir(it.dir("plugins")) }) - rename { "${pluginMeta.name.get()}.jar" } - } - return tasks.register("prepareServer") { - this.serverDir.set(serverDir) + this.serverDir.set(runServer.map { it.runDirectory.get() }) eula = serverConfiguration.eula onlineMode = serverConfiguration.onlineMode - dependsOn(copyServerCore, copyPlugins) - } - } - - private fun registerRunServerTask( - prepareServer: TaskProvider, - serverDir: Provider - ) { - tasks.register("runServer") { - workingDir(serverDir) - jvmArgs = serverConfiguration.buildJvmArgs() - bukkitArgs = serverConfiguration.bukkitArgs - dependsOn(prepareServer) } } - private fun registerBuildIdeRunTask(serverDir: Provider) { - tasks.register("buildIdeaRun") { - configurationName.set("${project.name}: Run server") - beforeRunTask.set("prepareServer") - vmParameters.set(serverConfiguration.buildJvmArgs(debug = false)) - programParameters.set(serverConfiguration.bukkitArgs) - configurationsDir.set(project.rootProject.layout.projectDirectory.dir(".idea/runConfigurations")) - jarPath.set(serverDir.map { it.file(ServerConstants.FILE_CORE).asFile }) + private fun registerBuildIdeRunTask(runServer: TaskProvider) { + tasks.register("buildIdeaRun") { + configurationName.set("Run Server [${project.name}]") + taskNames.add(runServer.name) } } } diff --git a/src/main/kotlin/server/ServerProperties.kt b/src/main/kotlin/server/ServerProperties.kt deleted file mode 100644 index 240687e..0000000 --- a/src/main/kotlin/server/ServerProperties.kt +++ /dev/null @@ -1,95 +0,0 @@ -package ru.endlesscode.bukkitgradle.server - -import org.gradle.api.InvalidUserDataException -import org.gradle.api.provider.Provider -import org.gradle.api.provider.ProviderFactory -import org.slf4j.LoggerFactory -import java.io.File -import java.util.* - -internal class ServerProperties(projectPath: File, private val providers: ProviderFactory) { - - val devServerDir: File - get() = getDir(DEV_SERVER_DIR) - - val buildToolsDir: File - get() = getDir(BUILD_TOOLS_DIR) - - private val logger = LoggerFactory.getLogger("ServerProperties") - private val properties = Properties() - - private val propertiesFile = File(projectPath, NAME) - - init { - if (propertiesFile.exists()) { - properties.load(propertiesFile.reader()) - } else { - loadDefaults("${projectPath.absolutePath}/build") - } - } - - private fun loadDefaults(defaultPath: String) { - logger.info("$NAME file not found. Creating default...") - - setDefault(DEV_SERVER_DIR, "$defaultPath/server") - setDefault(BUILD_TOOLS_DIR, "$defaultPath/buildtools") - - properties.store( - propertiesFile.writer(), - """ - This file should *NOT* be checked into Version Control Systems, - as it contains information specific to your local configuration. - """.trimIndent().trimEnd() - ) - } - - private fun setDefault(property: Property, defaultValue: String) { - if (!getEnvProvider(property.envVariable).isPresent) { - properties.setProperty(property.name, defaultValue) - } - } - - private fun getDir(property: Property): File { - return File(get(property)) - .absoluteFile - .also { it.mkdirs() } - } - - private fun get(property: Property): String { - val localProp = properties.getProperty(property.name) - val globalEnv = getEnvProvider(property.envVariable).orNull - return localProp ?: globalEnv ?: showError(property) - } - - private fun getEnvProvider(name: String): Provider { - return providers.environmentVariable(name) - } - - private fun showError(property: Property): Nothing { - logger.error( - """ - ${property.description} not found. It can be fixed by two ways: - 1.Define variable "${property.name}" in the $NAME file - 2.Define ${property.envVariable} environment variable - """.trimIndent() - ) - throw InvalidUserDataException() - } - - private data class Property(val name: String, val envVariable: String, val description: String) - - private companion object { - private const val NAME: String = "local.properties" - - private val BUILD_TOOLS_DIR: Property = Property( - name = "buildtools.dir", - envVariable = "BUKKIT_BUILDTOOLS_HOME", - description = "Path to BuildTools" - ) - private val DEV_SERVER_DIR: Property = Property( - name = "server.dir", - envVariable = "BUKKIT_DEV_SERVER_HOME", - description = "Path to Dev Server" - ) - } -} diff --git a/src/main/kotlin/server/extension/ServerConfiguration.kt b/src/main/kotlin/server/extension/ServerConfiguration.kt index e2b58eb..780b928 100644 --- a/src/main/kotlin/server/extension/ServerConfiguration.kt +++ b/src/main/kotlin/server/extension/ServerConfiguration.kt @@ -12,5 +12,5 @@ public interface ServerConfiguration { public val coreType: CoreType /** Returns arguments for JVM. */ - public fun buildJvmArgs(debug: Boolean = this.debug): List + public fun buildJvmArgs(): List } diff --git a/src/main/kotlin/server/extension/ServerConfigurationImpl.kt b/src/main/kotlin/server/extension/ServerConfigurationImpl.kt index 831b8c8..10bcfc8 100644 --- a/src/main/kotlin/server/extension/ServerConfigurationImpl.kt +++ b/src/main/kotlin/server/extension/ServerConfigurationImpl.kt @@ -11,11 +11,11 @@ public class ServerConfigurationImpl : ServerConfiguration { override var version: String? = null override var eula: Boolean = false override var onlineMode: Boolean = false - override var debug: Boolean = true + override var debug: Boolean = false override var encoding: String = "UTF-8" override var javaArgs: List = listOf("-Xmx1G") - override var bukkitArgs: List = listOf("nogui") + override var bukkitArgs: List = emptyList() override var coreType: CoreType = CoreType.SPIGOT @@ -48,8 +48,10 @@ public class ServerConfigurationImpl : ServerConfiguration { bukkitArgs = bukkitArgs + args.toList() } - override fun buildJvmArgs(debug: Boolean): List { - return listOfNotNull(DEBUG_ARGS.takeIf { debug }, "-Dfile.encoding=$encoding") + javaArgs + override fun buildJvmArgs(): List { + return listOfNotNull( + ACCEPT_EULA_ARGS.takeIf { eula }, + ) + javaArgs } override fun toString(): String { @@ -65,6 +67,6 @@ public class ServerConfigurationImpl : ServerConfiguration { } private companion object { - const val DEBUG_ARGS: String = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" + const val ACCEPT_EULA_ARGS: String = "-Dcom.mojang.eula.agree=true" } } diff --git a/src/main/kotlin/server/task/BuildServerCore.kt b/src/main/kotlin/server/task/BuildServerCore.kt deleted file mode 100644 index 30bc15d..0000000 --- a/src/main/kotlin/server/task/BuildServerCore.kt +++ /dev/null @@ -1,45 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.OutputFile -import org.gradle.process.CommandLineArgumentProvider -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import java.io.File - -@Suppress("LeakingThis") -public abstract class BuildServerCore : JavaExec() { - - @get:InputFile - public abstract val buildToolsFile: Property - - @get:Input - public abstract val version: Property - - @get:OutputFile - public val spigotFile: Provider = buildToolsFile.zip(version) { buildToolsFile, version -> - File(buildToolsFile.parentFile, "spigot-$version.jar") - } - - init { - group = TASKS_GROUP_BUKKIT - description = "Build server core" - - mainClass.set("-jar") - argumentProviders.add(BuildToolsArgumentsProvider()) - standardInput = System.`in` - - outputs.upToDateWhen { spigotFile.get().isFile } - } - - private inner class BuildToolsArgumentsProvider : CommandLineArgumentProvider { - - override fun asArguments(): Iterable { - val buildToolsPath = buildToolsFile.get().path - return listOf(buildToolsPath, "--rev", version.get()) - } - } -} diff --git a/src/main/kotlin/server/task/CreateIdeaGradleRunConfiguration.kt b/src/main/kotlin/server/task/CreateIdeaGradleRunConfiguration.kt new file mode 100644 index 0000000..4b9befe --- /dev/null +++ b/src/main/kotlin/server/task/CreateIdeaGradleRunConfiguration.kt @@ -0,0 +1,105 @@ +package ru.endlesscode.bukkitgradle.server.task + +import groovy.xml.MarkupBuilder +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.newInstance +import org.gradle.kotlin.dsl.withGroovyBuilder +import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT +import ru.endlesscode.bukkitgradle.server.util.Idea +import javax.inject.Inject + +/** Builds run configuration in IDEA .xml format and stores it to a file. */ +@Suppress("LeakingThis") +public abstract class CreateIdeaGradleRunConfiguration private constructor( + idea: Idea, +) : DefaultTask() { + + @get:Input + public abstract val configurationName: Property + + @get:Input + public abstract val projectPath: Property + + @get:Input + public abstract val vmOptions: ListProperty + + @get:Input + public abstract val scriptParameters: ListProperty + + @get:Input + public abstract val taskNames: ListProperty + + @get:Internal + public abstract val configurationsDir: DirectoryProperty + + @get:OutputFile + public val configurationFile: Provider = configurationsDir + .zip(configurationName, idea::runConfigurationFile) + + @Suppress("unused") + @Inject + internal constructor(objects: ObjectFactory) : this(objects.newInstance()) + + init { + group = TASKS_GROUP_BUKKIT + description = "Configure server run configuration for IDEA" + + vmOptions.convention(emptyList()) + scriptParameters.convention(emptyList()) + + configurationsDir.convention(idea.projectDir.map { it.dir(".run") }) + projectPath.convention(idea.relativePathOf(project.projectDir).orElse(project.projectDir.absolutePath)) + + val predicate = idea.isActive + onlyIf { predicate.get() } + } + + @TaskAction + public fun createJarRunConfiguration() { + val configurationName = configurationName.get() + val configurationFile = checkNotNull(configurationFile.orNull) { + "Cannot automatically determine the path to run configurations directory. " + + "Please set 'configurationsDir' manually" + }.asFile + configurationFile.parentFile.mkdirs() + + MarkupBuilder(configurationFile.writer()).withGroovyBuilder { + "component"("name" to "ProjectRunConfigurationManager") { + "configuration"( + "default" to false, + "name" to configurationName, + "type" to "GradleRunConfiguration", + "factoryName" to "Gradle", + "singleton" to true, + ) { + "ExternalSystemSettings" { + "option"("name" to "externalProjectPath", "value" to projectPath.get()) + "option"("name" to "externalSystemIdString", "value" to "GRADLE") + "option"("name" to "vmOptions", "value" to vmOptions.get().joinToString(" ")) + "option"("name" to "scriptParameters", "value" to scriptParameters.get().joinToString(" ")) + "option"("name" to "taskNames") { + "list" { + for (taskName in taskNames.get()) "option"("value" to taskName) + } + } + } + "ExternalSystemDebugServerProcess"(false) + "ExternalSystemReattachDebugProcess"(true) + "DebugAllEnabled"(false) + "RunAsTest"(false) + "method"("v" to 2) + } + } + } + } +} diff --git a/src/main/kotlin/server/task/CreateIdeaJarRunConfiguration.kt b/src/main/kotlin/server/task/CreateIdeaJarRunConfiguration.kt deleted file mode 100644 index ea0bd38..0000000 --- a/src/main/kotlin/server/task/CreateIdeaJarRunConfiguration.kt +++ /dev/null @@ -1,91 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import groovy.xml.MarkupBuilder -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.withGroovyBuilder -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import ru.endlesscode.bukkitgradle.server.util.Idea -import java.io.File -import javax.inject.Inject - -/** Builds and writes to file run configuration in IDEA .xml format. */ -@Suppress("LeakingThis") -public abstract class CreateIdeaJarRunConfiguration @Inject constructor( - providers: ProviderFactory -) : DefaultTask() { - - @get:Input - public abstract val configurationName: Property - - @get:Input - public abstract val vmParameters: ListProperty - - @get:Input - public abstract val programParameters: ListProperty - - @get:Input - public abstract val beforeRunTask: Property - - @get:Input - public abstract val jarPath: Property - - @get:Internal - public abstract val configurationsDir: DirectoryProperty - - @get:OutputFile - public val configurationFile: Provider = configurationsDir.zip(configurationName) { dir, name -> - dir.file("${Idea.fileNameSlug(name)}.xml") - } - - init { - group = TASKS_GROUP_BUKKIT - description = "Configure server run configuration for IDEA" - - vmParameters.convention(emptyList()) - programParameters.convention(emptyList()) - - onlyIf { Idea.isActive(providers) } - } - - @TaskAction - public fun createJarRunConfiguration() { - val configurationName = configurationName.get() - val configurationFile = configurationFile.get().asFile - - configurationFile.parentFile.mkdirs() - - MarkupBuilder(configurationFile.writer()).withGroovyBuilder { - "component"("name" to "ProjectRunConfigurationManager") { - "configuration"( - "default" to false, - "name" to configurationName, - "type" to "JarApplication", - "singleton" to true - ) { - "option"("name" to "JAR_PATH", "value" to jarPath.get()) - "option"("name" to "VM_PARAMETERS", "value" to vmParameters.get().joinToString(" ")) - "option"("name" to "PROGRAM_PARAMETERS", "value" to programParameters.get().joinToString(" ")) - "option"("name" to "WORKING_DIRECTORY", "value" to jarPath.get().parentFile) - "method"("v" to 2) { - "option"( - "name" to "Gradle.BeforeRunTask", - "enabled" to true, - "tasks" to beforeRunTask.get(), - "externalProjectPath" to "\$PROJECT_DIR$" - ) - } - } - } - } - } -} diff --git a/src/main/kotlin/server/task/DownloadPaperclip.kt b/src/main/kotlin/server/task/DownloadPaperclip.kt deleted file mode 100644 index 43fb1a3..0000000 --- a/src/main/kotlin/server/task/DownloadPaperclip.kt +++ /dev/null @@ -1,64 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import de.undercouch.gradle.tasks.download.Download -import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.StopExecutionException -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import java.io.File -import javax.inject.Inject - -@Suppress("LeakingThis") -public abstract class DownloadPaperclip @Inject constructor(providers: ProviderFactory) : Download() { - - @get:InputFile - public abstract val paperVersionsFile: Property - - @get:Input - public abstract val version: Property - - @get:OutputFile - public val paperclipFile: Provider = providers.provider { outputFiles.single() } - - init { - group = TASKS_GROUP_BUKKIT - description = "Download paperclip" - - src(paperVersionsFile.zip(version, ::extractPaperUrl)) - onlyIfModified(true) - } - - private fun extractPaperUrl(versionsFile: File, version: String): String { - if (!versionsFile.isFile) { - logger.warn("Paper versions file not downloaded, make sure that Gradle isn\'t running in offline mode.") - throw StopExecutionException() - } - - val versionsUrls = Json.decodeFromString(versionsFile.readText()).versions - val paperUrl = versionsUrls[version] - if (paperUrl == null) { - logger.warn( - """ - Paper v$version not found. - Supported paper versions: ${versionsUrls.keys}. - """.trimIndent() - ) - throw StopExecutionException() - } - - return paperUrl - } - - @Serializable - private data class PaperVersions( - val latest: String, - val versions: Map - ) -} diff --git a/src/main/kotlin/server/task/PrepareServer.kt b/src/main/kotlin/server/task/PrepareServer.kt index b0648a1..d9383d1 100644 --- a/src/main/kotlin/server/task/PrepareServer.kt +++ b/src/main/kotlin/server/task/PrepareServer.kt @@ -22,9 +22,6 @@ internal abstract class PrepareServer : DefaultTask() { @get:Input var onlineMode: Boolean = true - @get:OutputFile - val eulaFile: Provider = serverDir.map { it.file("eula.txt") } - @get:OutputFile val propertiesFile: Provider = serverDir.map { it.file("server.properties") } @@ -35,25 +32,9 @@ internal abstract class PrepareServer : DefaultTask() { @TaskAction fun prepareServer() { - resolveEula() resolveOnlineMode() } - private fun resolveEula() { - val eulaFile = eulaFile.get().asFile - if (!eulaFile.exists()) { - eulaFile.createNewFile() - } - - val properties = Properties() - properties.load(eulaFile.reader()) - properties.setProperty("eula", "$eula") - properties.store( - eulaFile.writer(), - "By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula)." - ) - } - private fun resolveOnlineMode() { val propsFile = propertiesFile.get().asFile if (!propsFile.exists()) { diff --git a/src/main/kotlin/server/task/RunServer.kt b/src/main/kotlin/server/task/RunServer.kt deleted file mode 100644 index fcc5089..0000000 --- a/src/main/kotlin/server/task/RunServer.kt +++ /dev/null @@ -1,28 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.JavaExec -import org.gradle.process.CommandLineArgumentProvider -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import ru.endlesscode.bukkitgradle.server.ServerConstants - -public open class RunServer : JavaExec() { - - @Internal - public var bukkitArgs: List = emptyList() - - init { - group = TASKS_GROUP_BUKKIT - description = "Run dev server." - - mainClass.set("-jar") - argumentProviders.add(RunServerArgumentsProvider()) - standardInput = System.`in` - } - - private inner class RunServerArgumentsProvider : CommandLineArgumentProvider { - override fun asArguments(): Iterable { - return listOf(ServerConstants.FILE_CORE) + bukkitArgs - } - } -} diff --git a/src/main/kotlin/server/util/Idea.kt b/src/main/kotlin/server/util/Idea.kt index 246a6ed..be5f1c2 100644 --- a/src/main/kotlin/server/util/Idea.kt +++ b/src/main/kotlin/server/util/Idea.kt @@ -1,19 +1,48 @@ package ru.endlesscode.bukkitgradle.server.util +import org.gradle.api.file.Directory +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider import org.gradle.api.provider.ProviderFactory +import java.io.File +import javax.inject.Inject -internal object Idea { +internal abstract class Idea @Inject constructor( + providers: ProviderFactory, + private val layout: ProjectLayout, +) { - private const val IDEA_ACTIVE: String = "idea.active" + val isActive: Provider = providers.systemProperty(IDEA_ACTIVE) + .map { it.toBoolean() } + .orElse(false) - fun isActive(providers: ProviderFactory): Boolean { - return providers.systemProperty(IDEA_ACTIVE).orNull == "true" + /** Directory containing '.idea/' */ + @Suppress("UnstableApiUsage") + val projectDir: Provider = + layout.dir(providers.provider { findProjectDir(layout.settingsDirectory.asFile) }) + + private tailrec fun findProjectDir(dir: File): File? { + if (dir.resolve(".idea").isDirectory) return dir + return findProjectDir(dir.parentFile ?: return null) + } + + fun relativePathOf(file: File): Provider = projectDir.map { + val relativePath = file.toRelativeString(it.asFile) + "\$PROJECT_DIR\$/$relativePath".trimEnd('/') } - @JvmStatic - fun fileNameSlug(name: String): String { - return name - .replace(Regex("[^\\x20-\\x7E]"), "") - .replace(Regex("[^a-zA-Z]"), "_") + fun runConfigurationFile(dir: Directory, name: String): RegularFile { + val extension = if (dir.asFile.endsWith(".idea/runConfiguration")) ".xml" else ".run.xml" + return dir.file("${sanitizeFileName(name)}$extension") + } + + companion object { + private const val IDEA_ACTIVE: String = "idea.active" + + private val charsToReplace = Regex("""[<>:\\"|?*]""") + + @JvmStatic + fun sanitizeFileName(name: String): String = name.replace(charsToReplace, "_") } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy index dcfe999..96a4336 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy @@ -21,12 +21,12 @@ class BukkitGradlePluginSpec extends PluginSpecification { where: apiVersion | jvmVersion - "1.11" | 8 - "1.12" | 11 - "1.16.4" | 11 - "1.16.5" | 16 + "1.16.5" | 8 "1.17" | 16 - "1.17.1" | 21 + "1.17.1" | 16 + "1.18" | 17 + "1.20.4" | 17 + "1.20.5" | 21 } def "when use custom repos extension - should add repos"() { diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy index 3d46017..69cb306 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy @@ -78,7 +78,7 @@ class PluginSpecification extends Specification { } protected def run(String... args) { - result = getRunner().withArguments(args.toList()).build() + result = getRunner().withArguments(args.toList() + "--stacktrace").build() } protected TaskOutcome taskOutcome(String task) { diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/extension/ServerConfigurationSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/extension/ServerConfigurationSpec.groovy index e19d3a0..ad49717 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/server/extension/ServerConfigurationSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/server/extension/ServerConfigurationSpec.groovy @@ -11,20 +11,13 @@ class ServerConfigurationSpec extends Specification { serverConfiguration = new ServerConfigurationImpl() } - void 'when build args - should return args with debug flags'() { + void 'when build args - should return args with eula flag'() { when: - def args = serverConfiguration.buildJvmArgs(true) + serverConfiguration.eula = true + def args = serverConfiguration.buildJvmArgs() then: - ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005", "-Dfile.encoding=UTF-8", "-Xmx1G"] == args - } - - void 'when build args - and debug disabled - should return args without debug flags'() { - when: - def args = serverConfiguration.buildJvmArgs(false) - - then: - ["-Dfile.encoding=UTF-8", "-Xmx1G"] == args + ["-Dcom.mojang.eula.agree=true", "-Xmx1G"] == args } void 'when set existing core - should set core successfully'() { diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CopyServerCoreSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CopyServerCoreSpec.groovy deleted file mode 100644 index 43ab9f2..0000000 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CopyServerCoreSpec.groovy +++ /dev/null @@ -1,52 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import ru.endlesscode.bukkitgradle.PluginSpecification - -class CopyServerCoreSpec extends PluginSpecification { - - private final static TASK_NAME = ':copyServerCore' - - def "when run copyServerCore - and selected spigot core - should also run build-tools build"() { - given: "spigot selected" - buildFile << """ - bukkit { - server.core = "spigot" - } - """.stripIndent() - - when: "run copyServerCore" - run(TASK_NAME, '--dry-run') - - then: "should download and run build-tools" - result.output.startsWith( - """\ - :downloadBuildTools SKIPPED - :buildServerCore SKIPPED - :copyServerCore SKIPPED - - BUILD SUCCESSFUL""".stripIndent() - ) - } - - def "when run copyServerCore - and selected paper core - should also download paperclip"() { - given: "paper selected" - buildFile << """ - bukkit { - server.core = "paper" - } - """.stripIndent() - - when: "run copyServerCore" - run(TASK_NAME, '--dry-run') - - then: "should download and run build-tools" - result.output.startsWith( - """\ - :downloadPaperVersions SKIPPED - :downloadPaperclip SKIPPED - :copyServerCore SKIPPED - - BUILD SUCCESSFUL""".stripIndent() - ) - } -} diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaGradleRunConfigurationSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaGradleRunConfigurationSpec.groovy new file mode 100644 index 0000000..27016b7 --- /dev/null +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaGradleRunConfigurationSpec.groovy @@ -0,0 +1,74 @@ +package ru.endlesscode.bukkitgradle.server.task + +import org.gradle.testkit.runner.TaskOutcome +import ru.endlesscode.bukkitgradle.PluginSpecification + +class CreateIdeaGradleRunConfigurationSpec extends PluginSpecification { + + private final static TASK_NAME = ':buildIdeaRun' + private final static IDEA_ACTIVE_PROPERTY = '-Didea.active=true' + + def setup() { + project.file(".idea").mkdirs() + } + + def "when run from IDEA - task should work"() { + when: + run(TASK_NAME, IDEA_ACTIVE_PROPERTY) + + then: + taskOutcome(TASK_NAME) == TaskOutcome.SUCCESS + } + + def "when run not from IDEA - task should be skipped"() { + when: + run(TASK_NAME) + + then: + taskOutcome(TASK_NAME) == TaskOutcome.SKIPPED + } + + def "when run from IDEA - should generate xml"() { + when: + run(TASK_NAME, IDEA_ACTIVE_PROPERTY) + + then: + file('.run/Run Server [test-plugin].run.xml').text == """ + + + + + + false + true + false + false + + + + """.stripIndent().trim() + } + + def "when run configurationsDir in .idea folder is used - should generate .xml extension"() { + given: + buildFile << ''' + tasks.buildIdeaRun { + configurationsDir = file(".idea/runConfigurations") + } + '''.stripIndent() + + when: + run(TASK_NAME, IDEA_ACTIVE_PROPERTY) + + then: + file('.idea/runConfigurations/Run Server [test-plugin].xml').exists() + } +} diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaJarRunConfigurationSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaJarRunConfigurationSpec.groovy deleted file mode 100644 index f123135..0000000 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/CreateIdeaJarRunConfigurationSpec.groovy +++ /dev/null @@ -1,55 +0,0 @@ -package ru.endlesscode.bukkitgradle.server.task - -import org.gradle.testkit.runner.TaskOutcome -import ru.endlesscode.bukkitgradle.PluginSpecification - -class CreateIdeaJarRunConfigurationSpec extends PluginSpecification { - - private final static TASK_NAME = ':buildIdeaRun' - private final static IDEA_ACTIVE_PROPERTY = '-Didea.active=true' - - def "when run from IDEA - task should work"() { - when: - run(TASK_NAME, IDEA_ACTIVE_PROPERTY) - - then: - taskOutcome(TASK_NAME) == TaskOutcome.SUCCESS - } - - def "when run not from IDEA - task should be skipped"() { - when: - run(TASK_NAME) - - then: - taskOutcome(TASK_NAME) == TaskOutcome.SKIPPED - } - - def "when run from IDEA - should generate xml"() { - given: - buildFile << ''' - bukkit { - version = '1.15.2' - } - '''.stripIndent() - - def serverDir = "${project.rootDir}/build/server/1.15.2" - - when: - run(TASK_NAME, IDEA_ACTIVE_PROPERTY) - - then: - file('.idea/runConfigurations/test_plugin__Run_server.xml').text == """\ - - - - - """.stripIndent().trim() - } -} diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/PrepareServerSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/PrepareServerSpec.groovy index b94229f..98a0433 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/PrepareServerSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/server/task/PrepareServerSpec.groovy @@ -1,6 +1,5 @@ package ru.endlesscode.bukkitgradle.server.task - import org.gradle.testkit.runner.TaskOutcome import ru.endlesscode.bukkitgradle.PluginSpecification @@ -8,70 +7,43 @@ class PrepareServerSpec extends PluginSpecification { private final static TASK_NAME = ':prepareServer' - def "when run prepareServer - should also run task dependencies"() { + def "when run prepareServer - should run successfully"() { when: "run prepareServer" - run(TASK_NAME, '-x', 'copyServer') - - then: "copyPlugins should be successful" - taskOutcome(':copyPlugins') == TaskOutcome.SUCCESS + run(TASK_NAME) - and: "task should be also successful" + then: "task should be successful" taskOutcome(TASK_NAME) == TaskOutcome.SUCCESS } - def "when run prepareServer again - should be uo-to-date"() { + def "when run prepareServer again - should be up-to-date"() { when: "run prepareServer" - run(TASK_NAME, '-x', 'copyServer') + run(TASK_NAME) then: "task should be successful" taskOutcome(TASK_NAME) == TaskOutcome.SUCCESS when: "run prepareServer again" - run(TASK_NAME, '-x', 'copyServer') + run(TASK_NAME) - then: "task should be uo-to-date" + then: "task should be up-to-date" taskOutcome(TASK_NAME) == TaskOutcome.UP_TO_DATE } - def "when run prepareServer - should set eula and online-mode"() { - given: "configured eula and online-mode" + def "when run prepareServer - should set online-mode"() { + given: "configured online-mode" buildFile << """ bukkit { - version = '1.16.2' run { - eula = true onlineMode = false } } """.stripIndent() - def serverDir = "build/server/1.16.2/" + def serverDir = "run/" when: "run prepareServer" - run(TASK_NAME, '-x', 'copyServer') + run(TASK_NAME) - then: "eula should be true" - file("$serverDir/eula.txt").readLines().contains("eula=true") - - and: "online-mode should be false" + then: "online-mode should be false" file("$serverDir/server.properties").readLines().contains("online-mode=false") } - - def "when run prepareServer - and there is shadow plugin - should use shadowJar task"() { - given: "configured eula and online-mode" - buildFile.text = """ - plugins { - id "ru.endlesscode.bukkitgradle" - id "com.gradleup.shadow" version "8.3.6" - } - - version = '1.0' - group = 'com.example.testplugin' - """.stripIndent() - - when: "run prepareServer" - run(TASK_NAME, '-x', 'copyServer') - - then: "should run shadowJar" - taskOutcome(":shadowJar") == TaskOutcome.SUCCESS - } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/server/util/IdeaSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/server/util/IdeaSpec.groovy index a101655..9010986 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/server/util/IdeaSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/server/util/IdeaSpec.groovy @@ -4,16 +4,17 @@ import spock.lang.Specification class IdeaSpec extends Specification { - def "test fileNameSlug"(String name, String slug) { + def "test sanitizeFileName"(String name, String slug) { when: - def nameSlug = Idea.fileNameSlug(name) + def nameSlug = Idea.sanitizeFileName(name) then: nameSlug == slug where: - name | slug - 'Run Server' | 'Run_Server' - 'my-plugin: Run Server' | 'my_plugin__Run_Server' + name | slug + 'Run Server' | 'Run Server' + 'my-plugin: Run Server' | 'my-plugin_ Run Server' + 'Run Server [my-plugin]' | 'Run Server [my-plugin]' } }