diff --git a/samples/mixed/bukkit-plugins/hello-world/build.gradle.kts b/samples/mixed/bukkit-plugins/hello-world/build.gradle.kts index ff05ffb..aae32f5 100644 --- a/samples/mixed/bukkit-plugins/hello-world/build.gradle.kts +++ b/samples/mixed/bukkit-plugins/hello-world/build.gradle.kts @@ -6,3 +6,7 @@ dependencies { implementation(project(":other-library")) implementation(project(":bukkit-plugins:common-library")) } + +concrete { + dependency(project(":bukkit-plugins:goodbye-world")) +} diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginExtension.kt b/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginExtension.kt new file mode 100644 index 0000000..71d7070 --- /dev/null +++ b/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginExtension.kt @@ -0,0 +1,12 @@ +package gay.pizza.foundation.concrete + +import org.gradle.api.DomainObjectSet +import org.gradle.api.Project + +interface ConcretePluginExtension { + val dependencies: DomainObjectSet + + fun dependency(project: Project) { + dependencies.add(project) + } +} diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginPlugin.kt b/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginPlugin.kt index 277b7ff..5907167 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginPlugin.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/ConcretePluginPlugin.kt @@ -9,6 +9,8 @@ class ConcretePluginPlugin : ConcreteBaseBukkitPlugin() { override fun apply(project: Project) { super.apply(project) + project.extensions.create("concrete", ConcretePluginExtension::class.java) + project.plugins.apply("com.github.johnrengelman.shadow") // During IDEA project import, if this code is active, it will print warnings. diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteExtension.kt b/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootExtension.kt similarity index 88% rename from src/main/kotlin/gay/pizza/foundation/concrete/ConcreteExtension.kt rename to src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootExtension.kt index 20b1c8d..d142547 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteExtension.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootExtension.kt @@ -2,7 +2,7 @@ package gay.pizza.foundation.concrete import org.gradle.api.provider.Property -interface ConcreteExtension { +interface ConcreteRootExtension { val paperServerVersionGroup: Property val paperApiVersion: Property val minecraftServerPath: Property diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootPlugin.kt b/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootPlugin.kt index 6ac640b..33b9d2b 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootPlugin.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/ConcreteRootPlugin.kt @@ -9,7 +9,7 @@ import java.nio.file.Paths class ConcreteRootPlugin : Plugin { override fun apply(project: Project) { project.apply(plugin = "base") - project.extensions.create("concrete") + project.extensions.create("concrete") val setupPaperServer = project.tasks.create("setupPaperServer") val runPaperServer = project.tasks.create("runPaperServer") runPaperServer.dependsOn(setupPaperServer) diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/ExtensibleManifest.kt b/src/main/kotlin/gay/pizza/foundation/concrete/ExtensibleManifest.kt new file mode 100644 index 0000000..01b8593 --- /dev/null +++ b/src/main/kotlin/gay/pizza/foundation/concrete/ExtensibleManifest.kt @@ -0,0 +1,55 @@ +package gay.pizza.foundation.concrete + +/** + * The extensible update manifest format. + */ +data class ExtensibleManifest( + /** + * The items the manifest describes. + */ + val items: List +) + +/** + * An item in the update manifest. + */ +data class ExtensibleManifestItem( + /** + * The name of the item. + */ + val name: String, + /** + * The type of item, for example "bukkit-plugin" + */ + val type: String, + /** + * The version of the item. + */ + val version: String, + /** + * The dependencies of the item. + */ + val dependencies: List, + /** + * The files that are required to install the item. + */ + val files: List +) + +/** + * A file built from the item. + */ +data class ExtensibleManifestItemFile( + /** + * The name of the file. + */ + val name: String, + /** + * A type of file. For example: "plugin-jar". + */ + val type: String, + /** + * The relative path to download the file. + */ + val path: String +) diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/Globals.kt b/src/main/kotlin/gay/pizza/foundation/concrete/Globals.kt index 683fd8c..c97b996 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/Globals.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/Globals.kt @@ -1,7 +1,9 @@ package gay.pizza.foundation.concrete import com.google.gson.Gson +import com.google.gson.GsonBuilder object Globals { - val gson = Gson() + val gsonPretty: Gson = GsonBuilder().setPrettyPrinting().create() + val gson: Gson = Gson() } diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/RunPaperServer.kt b/src/main/kotlin/gay/pizza/foundation/concrete/RunPaperServer.kt index df4854c..6037299 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/RunPaperServer.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/RunPaperServer.kt @@ -11,7 +11,7 @@ open class RunPaperServer : RunMinecraftServer() { @Internal override fun getServerDirectory(): File { - val concrete = project.extensions.getByType() + val concrete = project.extensions.getByType() return project.file(concrete.minecraftServerPath.get()) } diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/SetupMinecraftServer.kt b/src/main/kotlin/gay/pizza/foundation/concrete/SetupMinecraftServer.kt index 4c36daa..2c7c9ac 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/SetupMinecraftServer.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/SetupMinecraftServer.kt @@ -34,7 +34,7 @@ abstract class SetupMinecraftServer : DefaultTask() { Files.createSymbolicLink(pluginLinkFile.toPath(), pluginJarFile.toPath()) } - val concrete = project.extensions.getByType() + val concrete = project.extensions.getByType() if (concrete.acceptServerEula.isPresent && concrete.acceptServerEula.get()) { val writer = minecraftServerDirectory.resolve("eula.txt").bufferedWriter() val properties = Properties() diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/SetupPaperServer.kt b/src/main/kotlin/gay/pizza/foundation/concrete/SetupPaperServer.kt index c5b20da..9c3f5dc 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/SetupPaperServer.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/SetupPaperServer.kt @@ -18,7 +18,7 @@ open class SetupPaperServer : SetupMinecraftServer() { @TaskAction fun setupPaperServer() { - val concrete = project.extensions.getByType() + val concrete = project.extensions.getByType() val minecraftServerDirectory = getServerDirectory() val paperJarFile = project.file("${minecraftServerDirectory}/paper.jar") if (!paperJarFile.exists() || shouldUpdatePaperServer) { @@ -50,7 +50,7 @@ open class SetupPaperServer : SetupMinecraftServer() { @Internal override fun getServerDirectory(): File { - val concrete = project.extensions.getByType() + val concrete = project.extensions.getByType() return project.file(concrete.minecraftServerPath.get()) } } diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/UpdateManifestTask.kt b/src/main/kotlin/gay/pizza/foundation/concrete/UpdateManifestTask.kt index 2543e65..88c28f9 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/UpdateManifestTask.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/UpdateManifestTask.kt @@ -9,10 +9,10 @@ open class UpdateManifestTask : DefaultTask() { @TaskAction fun update() { val manifestsDir = ensureManifestsDirectory() - val updateFile = manifestsDir.resolve("update.json") - val rootPath = project.rootProject.rootDir.toPath() - val updateManifest = project.findPluginProjects().mapNotNull { project -> + val rootPath = project.projectDir.toPath() + val legacyUpdateManifest = project.findPluginProjects().mapNotNull { project -> val paths = project.shadowJarOutputs!!.allFilesRelativeToPath(rootPath) + if (paths.isNotEmpty()) { project.name to mapOf( "version" to project.version, @@ -21,7 +21,29 @@ open class UpdateManifestTask : DefaultTask() { } else null }.toMap() - Files.writeString(updateFile, Globals.gson.toJson(updateManifest)) + val legacyUpdateFile = manifestsDir.resolve("update.json") + Files.writeString(legacyUpdateFile, Globals.gson.toJson(legacyUpdateManifest)) + + val extensibleUpdateManifestItems = project.findPluginProjects().mapNotNull { project -> + val paths = project.shadowJarOutputs!!.allFilesRelativeToPath(rootPath) + + val dependencies = project.concretePluginExtension.dependencies.map { it.name } + ExtensibleManifestItem( + name = project.name, + type = "bukkit-plugin", + version = project.version.toString(), + dependencies = dependencies, + files = paths.map { it.toUnixString() } + ) + } + + val extensibleUpdateManifest = ExtensibleManifest( + items = extensibleUpdateManifestItems + ) + + val extensibleUpdateManifestFile = manifestsDir.resolve("manifest.json") + Files.writeString( + extensibleUpdateManifestFile, Globals.gsonPretty.toJson(extensibleUpdateManifest) + "\n") } private fun ensureManifestsDirectory(): Path { diff --git a/src/main/kotlin/gay/pizza/foundation/concrete/extensions.kt b/src/main/kotlin/gay/pizza/foundation/concrete/extensions.kt index 1f951b8..e757c38 100644 --- a/src/main/kotlin/gay/pizza/foundation/concrete/extensions.kt +++ b/src/main/kotlin/gay/pizza/foundation/concrete/extensions.kt @@ -30,20 +30,23 @@ internal val Project.shadowJarTask: ShadowJar? internal val Project.shadowJarOutputs: TaskOutputs? get() = shadowJarTask?.outputs -internal val Project.concreteRootExtension: ConcreteExtension +internal val Project.concreteRootExtension: ConcreteRootExtension get() = findTargetParent( - valid = { extensions.findByType(ConcreteExtension::class.java) != null }, - extract = { extensions.findByType(ConcreteExtension::class.java)!! }, + valid = { extensions.findByType(ConcreteRootExtension::class.java) != null }, + extract = { extensions.findByType(ConcreteRootExtension::class.java)!! }, error = { "Failed to find concrete root. Did you apply the concrete root plugin?" } ) +internal val Project.concretePluginExtension: ConcretePluginExtension + get() = extensions.getByType(ConcretePluginExtension::class.java) + /** * Finds the concrete root project, which is the first project in the project hierarchy * that has the concrete extension. */ internal val Project.concreteRootProject: Project get() = findTargetParent( - valid = { extensions.findByType(ConcreteExtension::class.java) != null }, + valid = { extensions.findByType(ConcreteRootExtension::class.java) != null }, extract = { this }, error = { "Failed to find concrete root. Did you apply the concrete root plugin?" } )