Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
root = true

[*.{kt,kts}]
max_line_length=120
ij_kotlin_imports_layout=*
indent_size = 4
indent_style = space
max_line_length = 120
ij_kotlin_imports_layout = *
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
insert_final_newline = true
2 changes: 1 addition & 1 deletion src/main/kotlin/creator/custom/TemplateDescriptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ data class TemplateDescriptor(

companion object {

const val FORMAT_VERSION = 1
const val FORMAT_VERSION = 2
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2025 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program 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 Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.creator.custom.derivation

import com.demonwav.mcdev.creator.custom.PropertyDerivation
import com.demonwav.mcdev.creator.custom.TemplateValidationReporter
import com.demonwav.mcdev.creator.custom.types.CreatorProperty
import com.demonwav.mcdev.util.MinecraftVersions
import com.demonwav.mcdev.util.SemanticVersion

class ExtractPaperApiVersionPropertyDerivation : ExtractVersionMajorMinorPropertyDerivation() {

override fun derive(parentValues: List<Any?>): Any? {
val from = parentValues[0] as SemanticVersion
if (from >= MinecraftVersions.MC1_20_5) {
return from
}

return super.derive(parentValues);
}

companion object : PropertyDerivationFactory {

override fun create(
reporter: TemplateValidationReporter,
parents: List<CreatorProperty<*>?>?,
derivation: PropertyDerivation
): PreparedDerivation? {
if (parents.isNullOrEmpty()) {
reporter.error("Expected a parent")
return null
}

if (!parents[0]!!.acceptsType(SemanticVersion::class.java)) {
reporter.error("First parent must produce a semantic version")
return null
}

return ExtractPaperApiVersionPropertyDerivation()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.demonwav.mcdev.creator.custom.TemplateValidationReporter
import com.demonwav.mcdev.creator.custom.types.CreatorProperty
import com.demonwav.mcdev.util.SemanticVersion

class ExtractVersionMajorMinorPropertyDerivation : PreparedDerivation {
open class ExtractVersionMajorMinorPropertyDerivation : PreparedDerivation {

override fun derive(parentValues: List<Any?>): Any? {
val from = parentValues[0] as SemanticVersion
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/creator/custom/model/StringList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ data class StringList(val values: List<String>) : List<String> by values {
@JvmOverloads
fun toString(separator: String, prefix: String = "", postfix: String = ""): String =
values.joinToString(separator, prefix, postfix)

fun toStringQuoted(): String =
values.joinToString(", ", transform = { '"' + it + '"' })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2025 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program 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 Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.creator.custom.types

import com.demonwav.mcdev.creator.custom.CreatorContext
import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor
import com.demonwav.mcdev.util.MinecraftVersions
import com.demonwav.mcdev.util.SemanticVersion
import com.intellij.ui.dsl.builder.Panel
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

open class PaperVersionCreatorProperty(
descriptor: TemplatePropertyDescriptor,
context: CreatorContext
) : SemanticVersionCreatorProperty(descriptor, context) {

@OptIn(DelicateCoroutinesApi::class)
companion object {
val paperVersions: Map<SemanticVersion, String>? = loadPaperVersions()?.associate { it to it.toString() }

fun loadPaperVersions(): List<SemanticVersion>? {
val client = HttpClient()
return runBlocking {
val response = client.get("https://fill.papermc.io/v3/projects/paper")
if (response.status.isSuccess()) {
val element = Json.parseToJsonElement(response.bodyAsText())
return@runBlocking element.jsonObject["versions"]?.jsonObject?.values
?.asSequence()
?.flatMap { it.jsonArray }
?.map { it.jsonPrimitive.content }
?.mapNotNull { SemanticVersion.tryParse(it) }
// only release versions
?.filter { ver -> ver.parts.all { it is SemanticVersion.Companion.VersionPart.ReleasePart } }
// nothing lower than 1.18.2 should be selectable
?.filter { it >= MinecraftVersions.MC1_18_2 }
?.toList()
?.sortedDescending()
} else {
return@runBlocking null
}
}
}
}

override fun buildUi(panel: Panel) {
super.buildDropdownMenu(panel, paperVersions)
}

class Factory : CreatorPropertyFactory {
override fun create(
descriptor: TemplatePropertyDescriptor,
context: CreatorContext
): CreatorProperty<*> = PaperVersionCreatorProperty(descriptor, context)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.demonwav.mcdev.creator.custom.CreatorContext
import com.demonwav.mcdev.creator.custom.PropertyDerivation
import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor
import com.demonwav.mcdev.creator.custom.TemplateValidationReporter
import com.demonwav.mcdev.creator.custom.derivation.ExtractPaperApiVersionPropertyDerivation
import com.demonwav.mcdev.creator.custom.derivation.ExtractVersionMajorMinorPropertyDerivation
import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation
import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation
Expand Down Expand Up @@ -64,6 +65,11 @@ open class SemanticVersionCreatorProperty(
ExtractVersionMajorMinorPropertyDerivation.create(reporter, parents, derives)
}

"extractPaperApiVersion" -> {
val parents = collectDerivationParents(reporter)
ExtractPaperApiVersionPropertyDerivation.create(reporter, parents, derives)
}

null -> {
SelectPropertyDerivation.create(reporter, emptyList(), derives)
}
Expand Down
66 changes: 35 additions & 31 deletions src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ abstract class SimpleCreatorProperty<T>(
valueType: Class<T>
) : CreatorProperty<T>(descriptor, context, valueType) {

private val options: Map<T, String>? = makeOptionsList()
val options: Map<T, String>? = makeOptionsList()

private fun makeOptionsList(): Map<T, String>? {
val map = when (val options = descriptor.options) {
Expand Down Expand Up @@ -78,37 +78,41 @@ abstract class SimpleCreatorProperty<T>(

override val graphProperty: GraphProperty<T> by lazy { graph.property(defaultValue) }

override fun buildUi(panel: Panel) {
if (isDropdown) {
if (graphProperty.get() !in options!!.keys) {
graphProperty.set(defaultValue)
protected fun buildDropdownMenu(panel: Panel, options: Map<T, String>?) {
if (graphProperty.get() !in options!!.keys) {
graphProperty.set(defaultValue)
}

panel.row(descriptor.translatedLabel) {
if (descriptor.forceDropdown == true) {
comboBox(options.keys, DropdownAutoRenderer())
.bindItem(graphProperty)
.enabled(descriptor.editable != false)
.also {
val component = it.component
ComboboxSpeedSearch.installOn(component)
val validation =
BuiltinValidations.isAnyOf(component::getSelectedItem, options.keys, component)
it.validationOnInput(validation)
it.validationOnApply(validation)
}
} else {
segmentedButton(options.keys) { text = options[it] ?: it.toString() }
.bind(graphProperty)
.enabled(descriptor.editable != false)
.maxButtonsCount(4)
.validation {
val message = MCDevBundle("creator.validation.invalid_option")
addInputRule(message) { it.selectedItem !in options.keys }
addApplyRule(message) { it.selectedItem !in options.keys }
}
}
}.propertyVisibility()
}

panel.row(descriptor.translatedLabel) {
if (descriptor.forceDropdown == true) {
comboBox(options.keys, DropdownAutoRenderer())
.bindItem(graphProperty)
.enabled(descriptor.editable != false)
.also {
val component = it.component
ComboboxSpeedSearch.installOn(component)
val validation =
BuiltinValidations.isAnyOf(component::getSelectedItem, options.keys, component)
it.validationOnInput(validation)
it.validationOnApply(validation)
}
} else {
segmentedButton(options.keys) { text = options[it] ?: it.toString() }
.bind(graphProperty)
.enabled(descriptor.editable != false)
.maxButtonsCount(4)
.validation {
val message = MCDevBundle("creator.validation.invalid_option")
addInputRule(message) { it.selectedItem !in options.keys }
addApplyRule(message) { it.selectedItem !in options.keys }
}
}
}.propertyVisibility()
override fun buildUi(panel: Panel) {
if (isDropdown) {
buildDropdownMenu(panel, options)
} else {
buildSimpleUi(panel)
}
Expand All @@ -125,7 +129,7 @@ abstract class SimpleCreatorProperty<T>(
isSelected: Boolean,
cellHasFocus: Boolean
): Component {
val label = options!![value] ?: value.toString()
val label = options?.get(value) ?: value.toString()
return super.getListCellRendererComponent(list, label, index, isSelected, cellHasFocus)
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@
<creatorPropertyType
implementation="com.demonwav.mcdev.creator.custom.types.NeoForgeVersionsCreatorProperty$Factory"
type="neoforge_versions"/>
<creatorPropertyType
implementation="com.demonwav.mcdev.creator.custom.types.PaperVersionCreatorProperty$Factory"
type="paper_versions"/>
<creatorPropertyType implementation="com.demonwav.mcdev.creator.custom.types.ParchmentCreatorProperty$Factory"
type="parchment"/>
<creatorPropertyType
Expand Down
11 changes: 11 additions & 0 deletions src/main/resources/messages/MinecraftDevelopment.properties
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ creator.ui.use_fabricapi.label=Use Fabric API
creator.ui.spongeapi_version.label=Sponge Version\:
creator.ui.velocity_version.label=Velocity Version\:
creator.ui.versions_download.label=Downloading versions...
creator.ui.use_version_catalog.label=Use Gradle version catalog\:
creator.ui.include_plugin_bootstrap.label=Include plugin bootstrap file\:
creator.ui.use_gremlin.label=Use Gremlin for dependency resolution\:
creator.ui.include_plugin_loader.label=Include plugin loader file\:
creator.ui.java_plugin_loader.label=Generate the plugin loader in Java\:
creator.ui.use_resource_factory_plugin.label=Use the resource-factory Gradle plugin\:
creator.ui.use_paperweight_userdev.label=Use the paperweight-userdev Gradle plugin\:
creator.ui.include_shadow_plugin.label=Include the shadow Gradle plugin\:
creator.ui.include_run_paper_plugin.label=Include the run-paper Gradle plugin\:
creator.ui.accept_eula.label=Add eula-agree runServer JVM flag\:
creator.ui.accept_eula.warning=By using this feature, you agree to the <a href='https://www.minecraft.net/en-us/eula'>Minecraft EULA</a>.

creator.ui.warn.no_yarn_to_mc_match=Unable to match Yarn versions to Minecraft version
creator.ui.warn.no_fabricapi_to_mc_match=Unable to match API versions to Minecraft version
Expand Down