User: rednesto Date: 24 Jan 24 07:21 Revision: e23ddbd3b83a43380a29b1e50c3ae9bcae966255 Summary: NeoForge support (#2196) * NeoForged support * 2024 license * Add Parchment mappings * Fix NeoGradle integration * License * Fix ktlint error * Allow using parchment mappings for older versions * Address reviews * Quote mod_id placeholder in mods.toml template See 59677b86ece5864084819bbbe3c616636e9d37f8 TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9047&personal=false The following binary files were skipped: src/main/resources/assets/icons/platform/NeoForge.png Index: readme.md =================================================================== --- readme.md (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ readme.md (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -137,6 +137,7 @@ - [![Sponge Icon](src/main/resources/assets/icons/platform/Sponge_dark.png?raw=true) **Sponge**](https://www.spongepowered.org/) - [![Architectury Icon](src/main/resources/assets/icons/platform/Architectury.png?raw=true) **Architectury**](https://github.com/architectury/architectury-api) - [![Forge Icon](src/main/resources/assets/icons/platform/Forge.png?raw=true) **Minecraft Forge**](https://forums.minecraftforge.net/) +- Neoforge - [![Fabric Icon](src/main/resources/assets/icons/platform/Fabric.png?raw=true) **Fabric**](https://fabricmc.net) - [![Mixins Icon](src/main/resources/assets/icons/platform/Mixins_dark.png?raw=true) **Mixins**](https://github.com/SpongePowered/Mixin) - [![BungeeCord Icon](src/main/resources/assets/icons/platform/BungeeCord.png?raw=true) **BungeeCord**](https://www.spigotmc.org/wiki/bungeecord/) ([![Waterfall Icon](src/main/resources/assets/icons/platform/Waterfall.png?raw=true) Waterfall](https://github.com/PaperMC/Waterfall)) Index: src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelBuilderImpl.groovy =================================================================== --- src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelBuilderImpl.groovy (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelBuilderImpl.groovy (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,72 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.mcp.gradle.tooling.neogradle + +import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelNG7 +import org.gradle.api.Project +import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService + +final class NeoGradle7ModelBuilderImpl implements ModelBuilderService { + + @Override + boolean canBuild(String modelName) { + return McpModelNG7.name == modelName + } + + @Override + Object buildAll(String modelName, Project project) { + def extension = project.extensions.findByName('minecraft') + if (extension == null) { + return null + } + + if (!project.plugins.findPlugin("net.neoforged.gradle.userdev")) { + return null + } + + // NG userdev + def runtimes = project.extensions.findByName('userDevRuntime').runtimes.get() + def neoforgeVersion = null + for (def entry in runtimes) { + neoforgeVersion = entry.value.specification.forgeVersion + break + } + if (neoforgeVersion == null) { + return null + } + + def mappingsFile = project.tasks.neoFormMergeMappings.output.get().asFile + + def accessTransformers = extension.accessTransformers.files.asList() + + //noinspection GroovyAssignabilityCheck + return new NeoGradle7ModelImpl(neoforgeVersion, mappingsFile, accessTransformers) + } + + @Override + ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { + return ErrorMessageBuilder.create( + project, e, "MinecraftDev import errors" + ).withDescription("Unable to build MinecraftDev MCP project configuration") + } +} Index: src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelImpl.groovy =================================================================== --- src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelImpl.groovy (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neogradle/NeoGradle7ModelImpl.groovy (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,43 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.mcp.gradle.tooling.neogradle + + +import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelNG7 +import groovy.transform.CompileStatic + +@CompileStatic +final class NeoGradle7ModelImpl implements McpModelNG7, Serializable { + + final String neoForgeVersion + final File mappingsFile + final List accessTransformers + + NeoGradle7ModelImpl( + final String neoForgeVersion, + final File mappingsFile, + final List accessTransformers + ) { + this.neoForgeVersion = neoForgeVersion + this.mappingsFile = mappingsFile + this.accessTransformers = accessTransformers + } +} Index: src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/McpModelNG7.java =================================================================== --- src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/McpModelNG7.java (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/McpModelNG7.java (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.mcp.gradle.tooling; + +import java.io.File; +import java.util.List; + +public interface McpModelNG7 { + String getNeoForgeVersion(); + File getMappingsFile(); + List getAccessTransformers(); +} Index: src/gradle-tooling-extension/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService =================================================================== --- src/gradle-tooling-extension/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/gradle-tooling-extension/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -1,5 +1,6 @@ com.demonwav.mcdev.platform.mcp.gradle.tooling.archloom.ArchitecturyModelBuilderImpl com.demonwav.mcdev.platform.mcp.gradle.tooling.fabricloom.FabricLoomModelBuilderImpl +com.demonwav.mcdev.platform.mcp.gradle.tooling.neogradle.NeoGradle7ModelBuilderImpl com.demonwav.mcdev.platform.mcp.gradle.tooling.vanillagradle.VanillaGradleModelBuilderImpl com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelFG2BuilderImpl com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelFG3BuilderImpl Index: src/main/kotlin/asset/PlatformAssets.kt =================================================================== --- src/main/kotlin/asset/PlatformAssets.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/asset/PlatformAssets.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -20,6 +20,8 @@ package com.demonwav.mcdev.asset +import com.intellij.util.IconUtil + @Suppress("unused") object PlatformAssets : Assets() { val MINECRAFT_ICON = loadIcon("/assets/icons/platform/Minecraft.png") @@ -62,6 +64,9 @@ val MIXIN_ICON_DARK = loadIcon("/assets/icons/platform/Mixins_dark.png") val MIXIN_ICON_2X_DARK = loadIcon("/assets/icons/platform/Mixins@2x_dark.png") + val NEOFORGE_ICON = IconUtil.scale(loadIcon("/assets/icons/platform/NeoForge.png"), null, 0.125f) + val NEOFORGE_ICON_2X = IconUtil.scale(loadIcon("/assets/icons/platform/NeoForge.png"), null, 0.25f) + val MCP_ICON = loadIcon("/assets/icons/platform/MCP.png") val MCP_ICON_2X = loadIcon("/assets/icons/platform/MCP@2x.png") val MCP_ICON_DARK = loadIcon("/assets/icons/platform/MCP_dark.png") Index: src/main/kotlin/creator/ParchmentStep.kt =================================================================== --- src/main/kotlin/creator/ParchmentStep.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/creator/ParchmentStep.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,168 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.creator + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.step.AbstractMcVersionChainStep +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.icons.AllIcons +import com.intellij.ide.wizard.AbstractNewProjectWizardStep +import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.observable.properties.ObservableMutableProperty +import com.intellij.openapi.observable.util.and +import com.intellij.openapi.observable.util.bindBooleanStorage +import com.intellij.openapi.observable.util.not +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.ui.content.AlertIcon +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon +import javax.swing.DefaultComboBoxModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class ParchmentStep(parent: NewProjectWizardStep) : AbstractNewProjectWizardStep(parent) { + + val useParchmentProperty = propertyGraph.property(false) + .bindBooleanStorage("${javaClass.name}.useParchment") + var useParchment by useParchmentProperty + + val parchmentVersionProperty = propertyGraph.property(null) + var parchmentVersion by parchmentVersionProperty + + val parchmentVersionsProperty = propertyGraph.property>(emptyList()) + var parchmentVersions by parchmentVersionsProperty + + val loadingVersionsProperty = propertyGraph.property(false) + var loadingVersions by loadingVersionsProperty + + val hasParchmentVersionProperty = propertyGraph.property(false) + var hasParchmentVersion by hasParchmentVersionProperty + + val includeOlderMcVersionsProperty = propertyGraph.property(false) + .bindBooleanStorage("${javaClass.name}.includeOlderMcVersions") + var includeOlderMcVersions by includeOlderMcVersionsProperty + + val includeSnapshotsVersionsProperty = propertyGraph.property(false) + .bindBooleanStorage("${javaClass.name}.includeSnapshotsVersions") + var includeSnapshotsVersions by includeSnapshotsVersionsProperty + + private val parchmentVersionsModel = DefaultComboBoxModel(emptyArray()) + private val mcVersionProperty: ObservableMutableProperty + + init { + storeToData() + @Suppress("UNCHECKED_CAST") + mcVersionProperty = findStep().getVersionProperty(0) + as ObservableMutableProperty + mcVersionProperty.afterChange { updateVersionsModel() } + + includeOlderMcVersionsProperty.afterChange { updateVersionsModel() } + includeSnapshotsVersionsProperty.afterChange { updateVersionsModel() } + + downloadVersions() + } + + private fun updateVersionsModel() { + val mcVersion = mcVersionProperty.get() + val versions = parchmentVersions.filter { version -> + if (!includeOlderMcVersions && version.mcVersion < mcVersion) { + return@filter false + } + + if (!includeSnapshotsVersions && version.parchmentVersion.contains("-SNAPSHOT")) { + return@filter false + } + + return@filter true + } + + hasParchmentVersion = versions.isNotEmpty() + + parchmentVersionsModel.removeAllElements() + parchmentVersionsModel.addAll(versions) + + parchmentVersionsModel.selectedItem = versions.firstOrNull { !it.parchmentVersion.contains('-') } + ?: versions.firstOrNull() + + loadingVersions = false + } + + private fun downloadVersions() { + loadingVersions = true + parchmentVersionsModel.removeAllElements() + application.executeOnPooledThread { + runBlocking { + val versions = ParchmentVersion.downloadData() + withContext(Dispatchers.Swing) { + parchmentVersions = versions + updateVersionsModel() + } + } + } + } + + override fun setupUI(builder: Panel) { + with(builder) { + row(MCDevBundle("creator.ui.parchment.label")) { + checkBox("") + .bindSelected(useParchmentProperty) + + comboBox(parchmentVersionsModel) + .enabledIf(useParchmentProperty and hasParchmentVersionProperty) + .bindItem(parchmentVersionProperty) + + cell(AsyncProcessIcon("$javaClass.parchmentVersions")) + .visibleIf(loadingVersionsProperty) + + label(MCDevBundle("creator.ui.parchment.no_version.message")) + .visibleIf(hasParchmentVersionProperty.not() and loadingVersionsProperty.not()) + .applyToComponent { icon = AlertIcon(AllIcons.General.Warning) } + } + + row(MCDevBundle("creator.ui.parchment.include.label")) { + checkBox(MCDevBundle("creator.ui.parchment.include.old_mc.label")) + .enabledIf(useParchmentProperty) + .bindSelected(includeOlderMcVersionsProperty) + checkBox(MCDevBundle("creator.ui.parchment.include.snapshots.label")) + .enabledIf(useParchmentProperty) + .bindSelected(includeSnapshotsVersionsProperty) + } + } + + super.setupUI(builder) + } + + override fun setupProject(project: Project) { + data.putUserData(USE_KEY, useParchment) + data.putUserData(VERSION_KEY, parchmentVersion) + } + + companion object { + val USE_KEY = Key.create("${ParchmentStep::class.java.name}.useParchment") + val VERSION_KEY = Key.create("${ParchmentStep::class.java}.parchmentVersion") + } +} Index: src/main/kotlin/creator/ParchmentVersion.kt =================================================================== --- src/main/kotlin/creator/ParchmentVersion.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/creator/ParchmentVersion.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,81 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.creator + +import com.demonwav.mcdev.util.SemanticVersion +import com.github.kittinunf.fuel.core.FuelError +import com.github.kittinunf.fuel.core.HttpException +import com.intellij.openapi.diagnostic.logger + +class ParchmentVersion private constructor(val mcVersion: SemanticVersion, val parchmentVersion: String) { + + private val presentableString by lazy { "$mcVersion - $parchmentVersion" } + + override fun toString(): String = presentableString + + companion object { + private val LOGGER = logger() + + suspend fun downloadData(limit: Int = 50): List { + val versions = mutableListOf() + val mcVersions = collectSupportedMcVersions() ?: return emptyList() + for (mcVersion in mcVersions) { + val url = "https://maven.parchmentmc.org/org/parchmentmc/data/parchment-$mcVersion/maven-metadata.xml" + try { + collectMavenVersions(url) + .mapTo(versions) { ParchmentVersion(mcVersion, it) } + if (versions.size > limit) { + return versions.subList(0, limit) + } + } catch (e: Exception) { + if (e !is FuelError || e.exception !is HttpException) { + LOGGER.error("Failed to retrieve Parchment version data from $url", e) + } + } + } + + return versions + } + + private suspend fun collectSupportedMcVersions(): List? { + try { + val baseUrl = "https://maven.parchmentmc.org/org/parchmentmc/data/" + val scrapeArtifactoryDirectoryListing = scrapeArtifactoryDirectoryListing(baseUrl) + return scrapeArtifactoryDirectoryListing + .asReversed() + .asSequence() + .filter { it.startsWith("parchment-") } + .mapNotNull { + val mcVersion = it.removePrefix("parchment-").removeSuffix("/") + SemanticVersion.tryParse(mcVersion) + } + .toList() + } catch (e: Exception) { + if (e is FuelError && e.exception is HttpException) { + return null + } + LOGGER.error("Failed to list supported Parchment Minecraft versions", e) + } + + return null + } + } +} Index: src/main/kotlin/creator/maven-repo-utils.kt =================================================================== --- src/main/kotlin/creator/maven-repo-utils.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/creator/maven-repo-utils.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,113 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.creator + +import com.demonwav.mcdev.update.PluginUtil +import com.github.kittinunf.fuel.core.FuelManager +import com.github.kittinunf.fuel.core.requests.suspendable +import java.io.IOException +import java.util.function.Predicate +import javax.xml.stream.XMLInputFactory +import javax.xml.stream.events.XMLEvent + +@Throws(IOException::class) +suspend fun collectMavenVersions(url: String, filter: Predicate = Predicate { true }): List { + val manager = FuelManager() + manager.proxy = selectProxy(url) + + val response = manager.get(url) + .header("User-Agent", PluginUtil.useragent) + .allowRedirects(true) + .suspendable() + .await() + + val result = mutableListOf() + response.body().toStream().use { stream -> + val inputFactory = XMLInputFactory.newInstance() + + @Suppress("UNCHECKED_CAST") + val reader = inputFactory.createXMLEventReader(stream) as Iterator + for (event in reader) { + if (!event.isStartElement) { + continue + } + val start = event.asStartElement() + val name = start.name.localPart + if (name != "version") { + continue + } + + val versionEvent = reader.next() + if (!versionEvent.isCharacters) { + continue + } + + val version = versionEvent.asCharacters().data + if (filter.test(version)) { + result += version + } + } + } + + return result +} + +@Throws(IOException::class) +suspend fun scrapeArtifactoryDirectoryListing(url: String): List { + val manager = FuelManager() + manager.proxy = selectProxy(url) + + val response = manager.get(url) + .header("User-Agent", PluginUtil.useragent) + .allowRedirects(true) + .suspendable() + .await() + + val result = mutableListOf() + response.body().toStream().use { stream -> + val inputFactory = XMLInputFactory.newInstance() + + @Suppress("UNCHECKED_CAST") + val reader = inputFactory.createXMLEventReader(stream) as Iterator + for (event in reader) { + if (!event.isStartElement) { + continue + } + val start = event.asStartElement() + val name = start.name.localPart + if (name != "a") { + continue + } + + val childPathEvent = reader.next() + if (!childPathEvent.isCharacters) { + continue + } + + val childPath = childPathEvent.asCharacters().data + if (childPath != "../") { + result += childPath + } + } + } + + return result +} Index: src/main/kotlin/creator/step/McVersionStep.kt =================================================================== --- src/main/kotlin/creator/step/McVersionStep.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/creator/step/McVersionStep.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -23,6 +23,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer import com.demonwav.mcdev.creator.findStep +import com.demonwav.mcdev.creator.storeToData import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.onShown @@ -74,6 +75,10 @@ const val MINECRAFT_VERSION = 0 } + init { + storeToData() + } + override fun setupUI(builder: Panel) { super.setupUI(builder) getVersionProperty(MINECRAFT_VERSION).afterChange { Index: src/main/kotlin/facet/MinecraftFacetEditorTabV2.kt =================================================================== --- src/main/kotlin/facet/MinecraftFacetEditorTabV2.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/facet/MinecraftFacetEditorTabV2.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -49,6 +49,7 @@ val paper = platformState(PlatformType.PAPER) val sponge = platformState(PlatformType.SPONGE) val forge = platformState(PlatformType.FORGE) + val neoforge = platformState(PlatformType.NEOFORGE) val fabric = platformState(PlatformType.FABRIC) val architectury = platformState(PlatformType.ARCHITECTURY) val mcp = platformState(PlatformType.MCP) @@ -103,6 +104,8 @@ }, ) + createRow(neoforge, "NeoForge", PlatformAssets.NEOFORGE_ICON_2X) + createRow( fabric, "Fabric", PlatformAssets.FABRIC_ICON_2X, { Index: src/main/kotlin/facet/MinecraftLibraryKinds.kt =================================================================== --- src/main/kotlin/facet/MinecraftLibraryKinds.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/facet/MinecraftLibraryKinds.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -31,6 +31,7 @@ import com.demonwav.mcdev.platform.forge.framework.FORGE_LIBRARY_KIND import com.demonwav.mcdev.platform.mcp.framework.MCP_LIBRARY_KIND import com.demonwav.mcdev.platform.mixin.framework.MIXIN_LIBRARY_KIND +import com.demonwav.mcdev.platform.neoforge.framework.NEOFORGE_LIBRARY_KIND import com.demonwav.mcdev.platform.sponge.framework.SPONGE_LIBRARY_KIND import com.demonwav.mcdev.platform.velocity.framework.VELOCITY_LIBRARY_KIND @@ -40,6 +41,7 @@ PAPER_LIBRARY_KIND, SPONGE_LIBRARY_KIND, FORGE_LIBRARY_KIND, + NEOFORGE_LIBRARY_KIND, FABRIC_LIBRARY_KIND, ARCHITECTURY_LIBRARY_KIND, MCP_LIBRARY_KIND, Index: src/main/kotlin/insight/PluginLineMarkerProvider.kt =================================================================== --- src/main/kotlin/insight/PluginLineMarkerProvider.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/insight/PluginLineMarkerProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -64,6 +64,7 @@ PlatformType.PAPER -> ThreeState.NO PlatformType.ARCHITECTURY -> ThreeState.YES PlatformType.FORGE -> ThreeState.YES + PlatformType.NEOFORGE -> ThreeState.YES PlatformType.FABRIC -> ThreeState.YES PlatformType.SPONGE -> ThreeState.NO PlatformType.BUNGEECORD -> ThreeState.NO Index: src/main/kotlin/insight/generation/MinecraftClassCreateAction.kt =================================================================== --- src/main/kotlin/insight/generation/MinecraftClassCreateAction.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/insight/generation/MinecraftClassCreateAction.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -27,6 +27,7 @@ import com.demonwav.mcdev.platform.fabric.FabricModuleType import com.demonwav.mcdev.platform.forge.ForgeModuleType import com.demonwav.mcdev.platform.mcp.McpModuleType +import com.demonwav.mcdev.platform.neoforge.NeoForgeModuleType import com.demonwav.mcdev.util.MinecraftTemplates import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion @@ -66,6 +67,7 @@ val module = directory.findModule() ?: return val isForge = MinecraftFacet.getInstance(module, ForgeModuleType) != null + val isNeoForge = MinecraftFacet.getInstance(module, NeoForgeModuleType) != null val isFabric = MinecraftFacet.getInstance(module, FabricModuleType) != null val mcVersion = MinecraftFacet.getInstance(module, McpModuleType)?.getSettings() ?.minecraftVersion?.let(SemanticVersion::parse) @@ -92,6 +94,16 @@ builder.addKind("Packet", icon, MinecraftTemplates.FORGE_1_18_PACKET_TEMPLATE) } } + + if (isNeoForge) { + val icon = PlatformAssets.NEOFORGE_ICON + builder.addKind("Block", icon, MinecraftTemplates.NEOFORGE_BLOCK_TEMPLATE) + builder.addKind("Enchantment", icon, MinecraftTemplates.NEOFORGE_ENCHANTMENT_TEMPLATE) + builder.addKind("Item", icon, MinecraftTemplates.NEOFORGE_ITEM_TEMPLATE) + builder.addKind("Mob Effect", icon, MinecraftTemplates.NEOFORGE_MOB_EFFECT_TEMPLATE) + builder.addKind("Packet", icon, MinecraftTemplates.NEOFORGE_PACKET_TEMPLATE) + } + if (isFabric) { val icon = PlatformAssets.FABRIC_ICON @@ -107,9 +119,10 @@ val module = psi?.findModule() ?: return false val isFabricMod = MinecraftFacet.getInstance(module, FabricModuleType) != null val isForgeMod = MinecraftFacet.getInstance(module, ForgeModuleType) != null + val isNeoForgeMod = MinecraftFacet.getInstance(module, NeoForgeModuleType) != null val hasMcVersion = MinecraftFacet.getInstance(module, McpModuleType)?.getSettings()?.minecraftVersion != null - return (isFabricMod || isForgeMod && hasMcVersion) && super.isAvailable(dataContext) + return (isFabricMod || isNeoForgeMod || isForgeMod && hasMcVersion) && super.isAvailable(dataContext) } override fun checkPackageExists(directory: PsiDirectory): Boolean { Index: src/main/kotlin/platform/PlatformType.kt =================================================================== --- src/main/kotlin/platform/PlatformType.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/platform/PlatformType.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -42,6 +42,8 @@ import com.demonwav.mcdev.platform.mcp.framework.MCP_LIBRARY_KIND import com.demonwav.mcdev.platform.mixin.MixinModuleType import com.demonwav.mcdev.platform.mixin.framework.MIXIN_LIBRARY_KIND +import com.demonwav.mcdev.platform.neoforge.NeoForgeModuleType +import com.demonwav.mcdev.platform.neoforge.framework.NEOFORGE_LIBRARY_KIND import com.demonwav.mcdev.platform.sponge.SpongeModuleType import com.demonwav.mcdev.platform.sponge.framework.SPONGE_LIBRARY_KIND import com.demonwav.mcdev.platform.velocity.VelocityModuleType @@ -65,6 +67,7 @@ WATERFALL(WaterfallModuleType, "waterfall.json", BUNGEECORD), VELOCITY(VelocityModuleType, "velocity.json"), MIXIN(MixinModuleType), + NEOFORGE(NeoForgeModuleType), MCP(McpModuleType), ADVENTURE(AdventureModuleType), ; @@ -82,7 +85,8 @@ companion object { fun removeParents(types: Set) = - types.filterNotNull() + types.asSequence() + .filterNotNull() .filter { type -> type.children.isEmpty() || types.none { type.children.contains(it) } } .toHashSet() @@ -100,6 +104,7 @@ WATERFALL_LIBRARY_KIND -> WATERFALL VELOCITY_LIBRARY_KIND -> VELOCITY ADVENTURE_LIBRARY_KIND -> ADVENTURE + NEOFORGE_LIBRARY_KIND -> NEOFORGE else -> null } } Index: src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt =================================================================== --- src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -61,6 +61,12 @@ "DEDICATED_SERVER", ), SideAnnotation( + "net.neoforged.api.distmarker.OnlyIn", + "net.neoforged.api.distmarker.Dist", + "CLIENT", + "DEDICATED_SERVER", + ), + SideAnnotation( "net.fabricmc.api.Environment", "net.fabricmc.api.EnvType", "CLIENT", Index: src/main/kotlin/platform/forge/util/ForgePackDescriptor.kt =================================================================== --- src/main/kotlin/platform/forge/util/ForgePackDescriptor.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/platform/forge/util/ForgePackDescriptor.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -54,6 +54,7 @@ val FORMAT_12 = ForgePackDescriptor(12, "") val FORMAT_15 = ForgePackDescriptor(15, "") val FORMAT_18 = ForgePackDescriptor(18, "") + val FORMAT_26 = ForgePackDescriptor(26, "") // See https://minecraft.gamepedia.com/Tutorials/Creating_a_resource_pack#.22pack_format.22 fun forMcVersion(version: SemanticVersion): ForgePackDescriptor? = when { @@ -67,7 +68,8 @@ version < MinecraftVersions.MC1_19_3 -> FORMAT_10 version < MinecraftVersions.MC1_20 -> FORMAT_12 version < MinecraftVersions.MC1_20_2 -> FORMAT_15 - version >= MinecraftVersions.MC1_20_2 -> FORMAT_18 + version < MinecraftVersions.MC1_20_3 -> FORMAT_18 + version >= MinecraftVersions.MC1_20_3 -> FORMAT_26 else -> null } } Index: src/main/kotlin/platform/forge/version/ForgeVersion.kt =================================================================== --- src/main/kotlin/platform/forge/version/ForgeVersion.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/platform/forge/version/ForgeVersion.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -20,16 +20,11 @@ package com.demonwav.mcdev.platform.forge.version -import com.demonwav.mcdev.creator.selectProxy -import com.demonwav.mcdev.update.PluginUtil +import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.sortVersions -import com.github.kittinunf.fuel.core.FuelManager -import com.github.kittinunf.fuel.core.requests.suspendable import com.intellij.openapi.diagnostic.logger import java.io.IOException -import javax.xml.stream.XMLInputFactory -import javax.xml.stream.events.XMLEvent class ForgeVersion private constructor(val versions: List) { @@ -70,45 +65,8 @@ suspend fun downloadData(): ForgeVersion? { try { val url = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml" - val manager = FuelManager() - manager.proxy = selectProxy(url) - - val response = manager.get(url) - .header("User-Agent", PluginUtil.useragent) - .suspendable() - .await() - - val result = mutableListOf() - response.body().toStream().use { stream -> - val inputFactory = XMLInputFactory.newInstance() - - @Suppress("UNCHECKED_CAST") - val reader = inputFactory.createXMLEventReader(stream) as Iterator - for (event in reader) { - if (!event.isStartElement) { - continue - } - val start = event.asStartElement() - val name = start.name.localPart - if (name != "version") { - continue - } - - val versionEvent = reader.next() - if (!versionEvent.isCharacters) { - continue - } - val version = versionEvent.asCharacters().data - val index = version.indexOf('-') - if (index == -1) { - continue - } - - result += version - } - } - - return ForgeVersion(result) + val versions = collectMavenVersions(url) { version -> version.indexOf('-') != -1 } + return ForgeVersion(versions) } catch (e: IOException) { LOGGER.error("Failed to retrieve Forge version data", e) } Index: src/main/kotlin/platform/mcp/gradle/McpProjectResolverExtension.kt =================================================================== --- src/main/kotlin/platform/mcp/gradle/McpProjectResolverExtension.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/platform/mcp/gradle/McpProjectResolverExtension.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -22,8 +22,10 @@ import com.demonwav.mcdev.platform.mcp.gradle.datahandler.McpModelFG2Handler import com.demonwav.mcdev.platform.mcp.gradle.datahandler.McpModelFG3Handler +import com.demonwav.mcdev.platform.mcp.gradle.datahandler.McpModelNG7Handler import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelFG2 import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelFG3 +import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelNG7 import com.demonwav.mcdev.util.runGradleTask import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.project.ModuleData @@ -36,7 +38,7 @@ // Register our custom Gradle tooling API model in IntelliJ's project resolver override fun getExtraProjectModelClasses(): Set> = - setOf(McpModelFG2::class.java, McpModelFG3::class.java) + setOf(McpModelFG2::class.java, McpModelFG3::class.java, McpModelNG7::class.java) override fun getToolingExtensionsClasses() = extraProjectModelClasses @@ -89,6 +91,6 @@ } companion object { - private val handlers = listOf(McpModelFG2Handler, McpModelFG3Handler) + private val handlers = listOf(McpModelFG2Handler, McpModelFG3Handler, McpModelNG7Handler) } } Index: src/main/kotlin/platform/mcp/gradle/datahandler/McpModelNG7Handler.kt =================================================================== --- src/main/kotlin/platform/mcp/gradle/datahandler/McpModelNG7Handler.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/mcp/gradle/datahandler/McpModelNG7Handler.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,76 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.mcp.gradle.datahandler + +import com.demonwav.mcdev.platform.mcp.McpModuleSettings +import com.demonwav.mcdev.platform.mcp.at.AtFileType +import com.demonwav.mcdev.platform.mcp.gradle.McpModelData +import com.demonwav.mcdev.platform.mcp.gradle.tooling.McpModelNG7 +import com.demonwav.mcdev.platform.mcp.srg.SrgType +import com.demonwav.mcdev.util.runWriteTaskLater +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.fileTypes.ExactFileNameMatcher +import com.intellij.openapi.fileTypes.FileTypeManager +import com.intellij.openapi.vfs.LocalFileSystem +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext + +object McpModelNG7Handler : McpModelDataHandler { + + override fun build( + gradleModule: IdeaModule, + node: DataNode, + resolverCtx: ProjectResolverContext, + ) { + val data = resolverCtx.getExtraProject(gradleModule, McpModelNG7::class.java) ?: return + + val state = McpModuleSettings.State( + data.neoForgeVersion.substringBeforeLast('.'), + null, + data.mappingsFile.absolutePath, + SrgType.TSRG, + data.neoForgeVersion, + ) + + val ats = data.accessTransformers + if (ats != null && ats.isNotEmpty()) { + runWriteTaskLater { + for (at in ats) { + val fileTypeManager = FileTypeManager.getInstance() + val atFile = LocalFileSystem.getInstance().findFileByIoFile(at) ?: continue + fileTypeManager.associate(AtFileType, ExactFileNameMatcher(atFile.name)) + } + } + } + + val modelData = McpModelData(node.data, state, null, data.accessTransformers) + node.createChild(McpModelData.KEY, modelData) + + for (child in node.children) { + val childData = child.data + if (childData is GradleSourceSetData) { + child.createChild(McpModelData.KEY, modelData.copy(module = childData)) + } + } + } +} Index: src/main/kotlin/platform/neoforge/NeoForgeFileIconProvider.kt =================================================================== --- src/main/kotlin/platform/neoforge/NeoForgeFileIconProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/NeoForgeFileIconProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,49 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge + +import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.facet.MinecraftFacet +import com.intellij.ide.FileIconProvider +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Iconable +import com.intellij.openapi.vfs.VirtualFile +import javax.swing.Icon + +class NeoForgeFileIconProvider : FileIconProvider { + + override fun getIcon(file: VirtualFile, @Iconable.IconFlags flags: Int, project: Project?): Icon? { + project ?: return null + + if (!MinecraftSettings.instance.isShowProjectPlatformIcons) { + return null + } + + val module = ModuleUtilCore.findModuleForFile(file, project) ?: return null + val forgeModule = MinecraftFacet.getInstance(module, NeoForgeModuleType) ?: return null + + if (file == forgeModule.mcmod) { + return forgeModule.icon + } + return null + } +} Index: src/main/kotlin/platform/neoforge/NeoForgeModule.kt =================================================================== --- src/main/kotlin/platform/neoforge/NeoForgeModule.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/NeoForgeModule.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,131 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge + +import com.demonwav.mcdev.asset.PlatformAssets +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.insight.generation.GenerationData +import com.demonwav.mcdev.inspection.IsCancelled +import com.demonwav.mcdev.platform.AbstractModule +import com.demonwav.mcdev.platform.PlatformType +import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants +import com.demonwav.mcdev.util.SourceType +import com.demonwav.mcdev.util.createVoidMethodWithParameterType +import com.demonwav.mcdev.util.nullable +import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions +import com.demonwav.mcdev.util.runWriteTaskLater +import com.demonwav.mcdev.util.waitForAllSmart +import com.intellij.json.JsonFileType +import com.intellij.lang.jvm.JvmModifier +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.fileTypes.FileTypeManager +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiMethodCallExpression +import org.jetbrains.uast.UClass +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.toUElementOfType + +class NeoForgeModule internal constructor(facet: MinecraftFacet) : AbstractModule(facet) { + + var mcmod by nullable { facet.findFile(NeoForgeConstants.MCMOD_INFO, SourceType.RESOURCE) } + private set + + override val moduleType = NeoForgeModuleType + override val type = PlatformType.NEOFORGE + override val icon = PlatformAssets.NEOFORGE_ICON + + override fun init() { + ApplicationManager.getApplication().executeOnPooledThread { + waitForAllSmart() + // Set mcmod.info icon + runWriteTaskLater { + FileTypeManager.getInstance().associatePattern(JsonFileType.INSTANCE, NeoForgeConstants.MCMOD_INFO) + FileTypeManager.getInstance().associatePattern(JsonFileType.INSTANCE, NeoForgeConstants.PACK_MCMETA) + } + } + } + + override fun isEventClassValid(eventClass: PsiClass, method: PsiMethod?): Boolean { + if (method == null) { + return NeoForgeConstants.FML_EVENT == eventClass.qualifiedName || + NeoForgeConstants.EVENTBUS_EVENT == eventClass.qualifiedName + } + + if (method.hasAnnotation(NeoForgeConstants.SUBSCRIBE_EVENT)) { + return NeoForgeConstants.EVENTBUS_EVENT == eventClass.qualifiedName + } + + // just default to true + return true + } + + override fun writeErrorMessageForEventParameter(eventClass: PsiClass, method: PsiMethod): String { + return formatWrongEventMessage( + NeoForgeConstants.EVENTBUS_EVENT, + NeoForgeConstants.SUBSCRIBE_EVENT, + NeoForgeConstants.EVENTBUS_EVENT == eventClass.qualifiedName, + ) + } + + private fun formatWrongEventMessage(expected: String, suggested: String, wrong: Boolean): String { + val base = "Parameter is not a subclass of $expected\n" + if (wrong) { + return base + "This method should be annotated with $suggested" + } + return base + "Compiling and running this listener may result in a runtime exception" + } + + override fun isStaticListenerSupported(method: PsiMethod) = true + + override fun generateEventListenerMethod( + containingClass: PsiClass, + chosenClass: PsiClass, + chosenName: String, + data: GenerationData?, + ): PsiMethod? { + val method = createVoidMethodWithParameterType(project, chosenName, chosenClass) ?: return null + val modifierList = method.modifierList + + modifierList.addAnnotation(NeoForgeConstants.SUBSCRIBE_EVENT) + + return method + } + + override fun shouldShowPluginIcon(element: PsiElement?): Boolean { + val identifier = element?.toUElementOfType() + ?: return false + + val psiClass = runCatchingKtIdeaExceptions { identifier.uastParent as? UClass } + ?: return false + + return !psiClass.hasModifier(JvmModifier.ABSTRACT) && + psiClass.findAnnotation(NeoForgeConstants.MOD_ANNOTATION) != null + } + + override fun checkUselessCancelCheck(expression: PsiMethodCallExpression): IsCancelled? = null + + override fun dispose() { + mcmod = null + super.dispose() + } +} Index: src/main/kotlin/platform/neoforge/NeoForgeModuleType.kt =================================================================== --- src/main/kotlin/platform/neoforge/NeoForgeModuleType.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/NeoForgeModuleType.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,52 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge + +import com.demonwav.mcdev.asset.PlatformAssets +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.platform.AbstractModuleType +import com.demonwav.mcdev.platform.PlatformType +import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants +import com.intellij.psi.PsiClass + +object NeoForgeModuleType : AbstractModuleType("", "") { + + private const val ID = "NEOFORGE_MODULE_TYPE" + + private val IGNORED_ANNOTATIONS = listOf( + NeoForgeConstants.MOD_ANNOTATION, + NeoForgeConstants.SUBSCRIBE_EVENT, + NeoForgeConstants.EVENT_BUS_SUBSCRIBER, + ) + private val LISTENER_ANNOTATIONS = listOf( + NeoForgeConstants.SUBSCRIBE_EVENT, + ) + + override val platformType = PlatformType.NEOFORGE + override val icon = PlatformAssets.NEOFORGE_ICON + override val id = ID + override val ignoredAnnotations = IGNORED_ANNOTATIONS + override val listenerAnnotations = LISTENER_ANNOTATIONS + override val isEventGenAvailable = true + + override fun generateModule(facet: MinecraftFacet) = NeoForgeModule(facet) + override fun getDefaultListenerName(psiClass: PsiClass): String = defaultNameForSubClassEvents(psiClass) +} Index: src/main/kotlin/platform/neoforge/creator/asset-steps.kt =================================================================== --- src/main/kotlin/platform/neoforge/creator/asset-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/creator/asset-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,158 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.creator + +import com.demonwav.mcdev.creator.addLicense +import com.demonwav.mcdev.creator.addTemplates +import com.demonwav.mcdev.creator.buildsystem.AbstractBuildSystemStep +import com.demonwav.mcdev.creator.buildsystem.AbstractRunBuildSystemStep +import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep +import com.demonwav.mcdev.creator.buildsystem.BuildSystemSupport +import com.demonwav.mcdev.creator.findStep +import com.demonwav.mcdev.creator.splitPackage +import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep +import com.demonwav.mcdev.creator.step.AbstractModIdStep +import com.demonwav.mcdev.creator.step.AbstractModNameStep +import com.demonwav.mcdev.creator.step.AbstractReformatFilesStep +import com.demonwav.mcdev.creator.step.AuthorsStep +import com.demonwav.mcdev.creator.step.DescriptionStep +import com.demonwav.mcdev.creator.step.LicenseStep +import com.demonwav.mcdev.creator.step.MainClassStep +import com.demonwav.mcdev.creator.step.UpdateUrlStep +import com.demonwav.mcdev.creator.step.UseMixinsStep +import com.demonwav.mcdev.creator.step.WebsiteStep +import com.demonwav.mcdev.platform.forge.util.ForgePackDescriptor +import com.demonwav.mcdev.util.MinecraftTemplates +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.project.Project + +class NeoForgeProjectFilesStep(parent: NewProjectWizardStep) : AbstractLongRunningAssetsStep(parent) { + override val description = "Creating NeoForge project files" + + override fun setupAssets(project: Project) { + val mcVersion = data.getUserData(NeoForgeVersionChainStep.MC_VERSION_KEY) ?: return + val forgeVersion = data.getUserData(NeoForgeVersionChainStep.NEOFORGE_VERSION_KEY) ?: return + val mainClass = data.getUserData(MainClassStep.KEY) ?: return + val (mainPackageName, mainClassName) = splitPackage(mainClass) + val buildSystemProps = findStep>() + val modId = data.getUserData(AbstractModIdStep.KEY) ?: return + val modName = data.getUserData(AbstractModNameStep.KEY) ?: return + val license = data.getUserData(LicenseStep.KEY) ?: return + val description = data.getUserData(DescriptionStep.KEY) ?: "" + val updateUrl = data.getUserData(UpdateUrlStep.KEY) ?: "" + val authors = data.getUserData(AuthorsStep.KEY) ?: emptyList() + val website = data.getUserData(WebsiteStep.KEY) ?: "" + val useMixins = data.getUserData(UseMixinsStep.KEY) ?: false + + val nextMcVersion = when (val part = mcVersion.parts.getOrNull(1)) { + // Extract the major version and increment (1.20.4 -> 1.21), as is done manually in the MDK + is SemanticVersion.Companion.VersionPart.ReleasePart -> (part.version + 1).toString() + null -> "?" + else -> part.versionString + } + + val packDescriptor = ForgePackDescriptor.forMcVersion(mcVersion) ?: ForgePackDescriptor.FORMAT_3 + + assets.addTemplateProperties( + "PACKAGE_NAME" to mainPackageName, + "CLASS_NAME" to mainClassName, + "ARTIFACT_ID" to buildSystemProps.artifactId, + "MOD_ID" to modId, + "MOD_NAME" to modName, + "MOD_VERSION" to buildSystemProps.version, + "NEOFORGE_SPEC_VERSION" to forgeVersion.parts[0].versionString, + "MC_VERSION" to mcVersion, + "MC_NEXT_VERSION" to "1.$nextMcVersion", + "LICENSE" to license, + "DESCRIPTION" to description, + "MIXIN_CONFIG" to if (useMixins) "$modId.mixins.json" else null, + "PACK_FORMAT" to packDescriptor.format, + "PACK_COMMENT" to packDescriptor.comment, + ) + + if (updateUrl.isNotBlank()) { + assets.addTemplateProperties("UPDATE_URL" to updateUrl) + } + + if (authors.isNotEmpty()) { + assets.addTemplateProperties("AUTHOR_LIST" to authors.joinToString(", ")) + } + + if (website.isNotBlank()) { + assets.addTemplateProperties("WEBSITE" to website) + } + + val mainClassTemplate = MinecraftTemplates.NEOFORGE_MAIN_CLASS_TEMPLATE + + assets.addTemplates( + project, + "src/main/java/${mainClass.replace('.', '/')}.java" to mainClassTemplate, + "src/main/resources/pack.mcmeta" to MinecraftTemplates.NEOFORGE_PACK_MCMETA_TEMPLATE, + "src/main/resources/META-INF/mods.toml" to MinecraftTemplates.NEOFORGE_MODS_TOML_TEMPLATE, + ) + + val configPath = if (mainPackageName != null) { + "src/main/java/${mainPackageName.replace('.', '/')}/Config.java" + } else { + "src/main/java/Config.java" + } + assets.addTemplates(project, configPath to MinecraftTemplates.NEOFORGE_CONFIG_TEMPLATE) + + assets.addLicense(project) + } +} + +// Needs to be a separate step from above because of PACKAGE_NAME being different +class NeoForgeMixinsJsonStep(parent: NewProjectWizardStep) : AbstractLongRunningAssetsStep(parent) { + override val description = "Creating mixins json" + + override fun setupAssets(project: Project) { + val useMixins = data.getUserData(UseMixinsStep.KEY) ?: false + if (useMixins) { + val modId = data.getUserData(AbstractModIdStep.KEY) ?: return + val buildSystemProps = findStep>() + assets.addTemplateProperties( + "PACKAGE_NAME" to "${buildSystemProps.groupId}.$modId.mixin", + "MOD_ID" to buildSystemProps.artifactId, + ) + val mixinsJsonFile = "src/main/resources/$modId.mixins.json" + assets.addTemplates(project, mixinsJsonFile to MinecraftTemplates.NEOFORGE_MIXINS_JSON_TEMPLATE) + } + } +} + +class NeoForgeReformatPackDescriptorStep(parent: NewProjectWizardStep) : AbstractReformatFilesStep(parent) { + + override fun addFilesToReformat() { + addFileToReformat("src/main/resources/pack.mcmeta") + } +} + +class NeoForgeBuildSystemStep(parent: NewProjectWizardStep) : AbstractBuildSystemStep(parent) { + override val platformName = "NeoForge" +} + +class NeoForgePostBuildSystemStep( + parent: NewProjectWizardStep, +) : AbstractRunBuildSystemStep(parent, NeoForgeBuildSystemStep::class.java) { + override val step = BuildSystemSupport.POST_STEP +} Index: src/main/kotlin/platform/neoforge/creator/gradle-steps.kt =================================================================== --- src/main/kotlin/platform/neoforge/creator/gradle-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/creator/gradle-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,153 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.creator + +import com.demonwav.mcdev.creator.EmptyStep +import com.demonwav.mcdev.creator.ParchmentStep +import com.demonwav.mcdev.creator.addGradleGitignore +import com.demonwav.mcdev.creator.addTemplates +import com.demonwav.mcdev.creator.buildsystem.AbstractRunGradleTaskStep +import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep +import com.demonwav.mcdev.creator.buildsystem.BuildSystemSupport +import com.demonwav.mcdev.creator.buildsystem.GRADLE_VERSION_KEY +import com.demonwav.mcdev.creator.buildsystem.GradleImportStep +import com.demonwav.mcdev.creator.buildsystem.GradleWrapperStep +import com.demonwav.mcdev.creator.buildsystem.addGradleWrapperProperties +import com.demonwav.mcdev.creator.findStep +import com.demonwav.mcdev.creator.gitEnabled +import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep +import com.demonwav.mcdev.creator.step.AbstractModIdStep +import com.demonwav.mcdev.creator.step.AbstractModNameStep +import com.demonwav.mcdev.creator.step.AuthorsStep +import com.demonwav.mcdev.creator.step.DescriptionStep +import com.demonwav.mcdev.creator.step.LicenseStep +import com.demonwav.mcdev.creator.step.NewProjectWizardChainStep.Companion.nextStep +import com.demonwav.mcdev.util.MinecraftTemplates +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.util.lang.JavaVersion + +private val ngWrapperVersion = SemanticVersion.release(8, 4) + +const val MAGIC_RUN_CONFIGS_FILE = ".hello_from_mcdev" + +class NeoForgeGradleSupport : BuildSystemSupport { + override val preferred = true + + override fun createStep(step: String, parent: NewProjectWizardStep): NewProjectWizardStep { + return when (step) { + BuildSystemSupport.PRE_STEP -> NeoForgeGradleFilesStep(parent).nextStep(::GradleWrapperStep) + BuildSystemSupport.POST_STEP -> NeoForgeCompileJavaStep(parent).nextStep(::GradleImportStep) + else -> EmptyStep(parent) + } + } +} + +class NeoForgeGradleFilesStep(parent: NewProjectWizardStep) : AbstractLongRunningAssetsStep(parent) { + override val description = "Creating Gradle files" + + override fun setupAssets(project: Project) { + val mcVersion = data.getUserData(NeoForgeVersionChainStep.MC_VERSION_KEY) ?: return + val neoforgeVersion = data.getUserData(NeoForgeVersionChainStep.NEOFORGE_VERSION_KEY) ?: return + val modId = data.getUserData(AbstractModIdStep.KEY) ?: return + val modName = data.getUserData(AbstractModNameStep.KEY) ?: return + val buildSystemProps = findStep>() + val javaVersion = context.projectJdk?.versionString?.let(JavaVersion::parse) + val authors = data.getUserData(AuthorsStep.KEY) ?: emptyList() + val description = data.getUserData(DescriptionStep.KEY) ?: return + val license = data.getUserData(LicenseStep.KEY) ?: return + val parchment = findStep() + val mcNextVersionPart = mcVersion.parts[1] + val mcNextVersion = if (mcNextVersionPart is SemanticVersion.Companion.VersionPart.ReleasePart) { + SemanticVersion.release(1, mcNextVersionPart.version + 1) + } else { + mcVersion + } + + data.putUserData(GRADLE_VERSION_KEY, ngWrapperVersion) + + assets.addTemplateProperties( + "MOD_ID" to modId, + "MOD_NAME" to modName, + "MC_VERSION" to mcVersion, + "MC_NEXT_VERSION" to mcNextVersion, + "NEOFORGE_VERSION" to neoforgeVersion, + "NEOFORGE_SPEC_VERSION" to neoforgeVersion.parts[0].versionString, + "GROUP_ID" to buildSystemProps.groupId, + "ARTIFACT_ID" to buildSystemProps.artifactId, + "MOD_VERSION" to buildSystemProps.version, + "DESCRIPTION" to description, + "AUTHOR_LIST" to authors.joinToString(", "), + "LICENSE" to license.id, + "HAS_DATA" to "true" + ) + + if (parchment.useParchment) { + assets.addTemplateProperties( + "PARCHMENT_MC_VERSION" to parchment.parchmentVersion?.mcVersion.toString(), + "PARCHMENT_VERSION" to parchment.parchmentVersion?.parchmentVersion, + ) + } + + if (javaVersion != null) { + assets.addTemplateProperties("JAVA_VERSION" to javaVersion.feature) + } + + if (neoforgeVersion >= SemanticVersion.release(39, 0, 88)) { + assets.addTemplateProperties("GAME_TEST_FRAMEWORK" to "true") + } + + assets.addTemplates( + project, + "build.gradle" to MinecraftTemplates.NEOFORGE_BUILD_GRADLE_TEMPLATE, + "gradle.properties" to MinecraftTemplates.NEOFORGE_GRADLE_PROPERTIES_TEMPLATE, + "settings.gradle" to MinecraftTemplates.NEOFORGE_SETTINGS_GRADLE_TEMPLATE, + ) + + assets.addGradleWrapperProperties(project) + + if (gitEnabled) { + assets.addGradleGitignore(project) + } + + WriteAction.runAndWait { + val dir = VfsUtil.createDirectoryIfMissing( + LocalFileSystem.getInstance(), + "${assets.outputDirectory}/.gradle", + ) + ?: throw IllegalStateException("Unable to create .gradle directory") + val file = dir.findOrCreateChildData(this, MAGIC_RUN_CONFIGS_FILE) + val fileContents = buildSystemProps.artifactId + "\n" + + mcVersion + "\n" + + neoforgeVersion + "\n" + + "genIntellijRuns" + VfsUtil.saveText(file, fileContents) + } + } +} + +class NeoForgeCompileJavaStep(parent: NewProjectWizardStep) : AbstractRunGradleTaskStep(parent) { + override val task = "compileJava" +} Index: src/main/kotlin/platform/neoforge/creator/ui-steps.kt =================================================================== --- src/main/kotlin/platform/neoforge/creator/ui-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/creator/ui-steps.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,112 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.creator + +import com.demonwav.mcdev.creator.ParchmentStep +import com.demonwav.mcdev.creator.platformtype.ModPlatformStep +import com.demonwav.mcdev.creator.step.AbstractCollapsibleStep +import com.demonwav.mcdev.creator.step.AbstractLatentStep +import com.demonwav.mcdev.creator.step.AbstractMcVersionChainStep +import com.demonwav.mcdev.creator.step.AuthorsStep +import com.demonwav.mcdev.creator.step.DescriptionStep +import com.demonwav.mcdev.creator.step.ForgeStyleModIdStep +import com.demonwav.mcdev.creator.step.LicenseStep +import com.demonwav.mcdev.creator.step.MainClassStep +import com.demonwav.mcdev.creator.step.ModNameStep +import com.demonwav.mcdev.creator.step.NewProjectWizardChainStep.Companion.nextStep +import com.demonwav.mcdev.creator.step.UpdateUrlStep +import com.demonwav.mcdev.creator.step.UseMixinsStep +import com.demonwav.mcdev.creator.step.WebsiteStep +import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion +import com.demonwav.mcdev.util.MinecraftVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.demonwav.mcdev.util.asyncIO +import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.util.IncorrectOperationException +import kotlinx.coroutines.coroutineScope + +private val minSupportedMcVersion = MinecraftVersions.MC1_20_2 + +class NeoForgePlatformStep(parent: ModPlatformStep) : AbstractLatentStep(parent) { + override val description = "fetch NeoForge versions" + + override suspend fun computeData() = coroutineScope { + asyncIO { NeoForgeVersion.downloadData() }.await() + } + + override fun createStep(data: NeoForgeVersion) = NeoForgeVersionChainStep(this, data) + .nextStep(::ForgeStyleModIdStep) + .nextStep(::ModNameStep) + .nextStep(::MainClassStep) + .nextStep(::UseMixinsStep) + .nextStep(::LicenseStep) + .nextStep(::NeoForgeOptionalSettingsStep) + .nextStep(::NeoForgeBuildSystemStep) + .nextStep(::NeoForgeProjectFilesStep) + .nextStep(::NeoForgeMixinsJsonStep) + .nextStep(::NeoForgePostBuildSystemStep) + .nextStep(::NeoForgeReformatPackDescriptorStep) + + class Factory : ModPlatformStep.Factory { + override val name = "NeoForge" + override fun createStep(parent: ModPlatformStep) = NeoForgePlatformStep(parent) + } +} + +class NeoForgeVersionChainStep( + parent: NewProjectWizardStep, + private val neoforgeVersionData: NeoForgeVersion, +) : AbstractMcVersionChainStep(parent, "NeoForge Version:") { + companion object { + private const val NEOFORGE_VERSION = 1 + + val MC_VERSION_KEY = Key.create("${NeoForgeVersionChainStep::class.java}.mcVersion") + val NEOFORGE_VERSION_KEY = + Key.create("${NeoForgeVersionChainStep::class.java}.neoforgeVersion") + } + + override fun getAvailableVersions(versionsAbove: List>): List> { + return when (versionsAbove.size) { + MINECRAFT_VERSION -> neoforgeVersionData.sortedMcVersions.filter { it >= minSupportedMcVersion } + NEOFORGE_VERSION -> + neoforgeVersionData.getNeoForgeVersions(versionsAbove[MINECRAFT_VERSION] as SemanticVersion) + else -> throw IncorrectOperationException() + } + } + + override fun setupProject(project: Project) { + super.setupProject(project) + data.putUserData(MC_VERSION_KEY, getVersion(MINECRAFT_VERSION) as SemanticVersion) + data.putUserData(NEOFORGE_VERSION_KEY, getVersion(NEOFORGE_VERSION) as SemanticVersion) + } +} + +class NeoForgeOptionalSettingsStep(parent: NewProjectWizardStep) : AbstractCollapsibleStep(parent) { + override val title = "Optional Settings" + + override fun createStep() = DescriptionStep(this) + .nextStep(::AuthorsStep) + .nextStep(::WebsiteStep) + .nextStep(::UpdateUrlStep) + .nextStep(::ParchmentStep) +} Index: src/main/kotlin/platform/neoforge/framework/NeoForgeLibraryKind.kt =================================================================== --- src/main/kotlin/platform/neoforge/framework/NeoForgeLibraryKind.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/framework/NeoForgeLibraryKind.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.framework + +import com.demonwav.mcdev.util.libraryKind +import com.intellij.openapi.roots.libraries.LibraryKind + +val NEOFORGE_LIBRARY_KIND: LibraryKind = libraryKind("neoforge-library") Index: src/main/kotlin/platform/neoforge/framework/NeoForgePresentationProvider.kt =================================================================== --- src/main/kotlin/platform/neoforge/framework/NeoForgePresentationProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/framework/NeoForgePresentationProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,43 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.framework + +import com.demonwav.mcdev.asset.PlatformAssets +import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants +import com.demonwav.mcdev.util.localFile +import com.intellij.framework.library.LibraryVersionProperties +import com.intellij.openapi.roots.libraries.LibraryPresentationProvider +import com.intellij.openapi.util.io.JarUtil +import com.intellij.openapi.vfs.VirtualFile + +class NeoForgePresentationProvider : LibraryPresentationProvider(NEOFORGE_LIBRARY_KIND) { + + override fun getIcon(properties: LibraryVersionProperties?) = PlatformAssets.NEOFORGE_ICON + + override fun detect(classesRoots: List): LibraryVersionProperties? { + for (classesRoot in classesRoots) { + if (JarUtil.containsClass(classesRoot.localFile, NeoForgeConstants.MOD_ANNOTATION)) { + return LibraryVersionProperties() + } + } + return null + } +} Index: src/main/kotlin/platform/neoforge/gradle/NeoForgeRunConfigDataService.kt =================================================================== --- src/main/kotlin/platform/neoforge/gradle/NeoForgeRunConfigDataService.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/gradle/NeoForgeRunConfigDataService.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,158 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.gradle + +import com.demonwav.mcdev.platform.neoforge.creator.MAGIC_RUN_CONFIGS_FILE +import com.demonwav.mcdev.util.invokeAndWait +import com.demonwav.mcdev.util.localFile +import com.demonwav.mcdev.util.runGradleTaskAndWait +import com.intellij.execution.RunManager +import com.intellij.execution.RunManagerListener +import com.intellij.execution.RunnerAndConfigurationSettings +import com.intellij.execution.application.ApplicationConfiguration +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService +import com.intellij.openapi.module.Module +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vfs.LocalFileSystem +import java.nio.file.Files +import java.nio.file.Paths +import java.util.concurrent.atomic.AtomicInteger +import org.jetbrains.plugins.gradle.util.GradleConstants + +class NeoForgeRunConfigDataService : AbstractProjectDataService() { + + override fun getTargetDataKey() = ProjectKeys.PROJECT + + override fun postProcess( + toImport: Collection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider, + ) { + if (projectData == null || projectData.owner != GradleConstants.SYSTEM_ID) { + return + } + + val baseDir = project.guessProjectDir() ?: return + val baseDirPath = baseDir.localFile.toPath() + val hello = baseDirPath.resolve(Paths.get(".gradle", MAGIC_RUN_CONFIGS_FILE)) + if (!Files.isRegularFile(hello)) { + return + } + + val lines = Files.readAllLines(hello, Charsets.UTF_8) + if (lines.size < 4) { + return + } + + val (moduleName, mcVersion, forgeVersion, task) = lines + + val moduleMap = modelsProvider.modules.associateBy { it.name } + val module = moduleMap[moduleName] ?: return + + // We've found the module we were expecting, so we can assume the project imported correctly + Files.delete(hello) + + genIntellijRuns(project, moduleMap, module, task) + } + + private fun genIntellijRuns( + project: Project, + moduleMap: Map, + module: Module, + task: String, + ) { + val mainModule = findMainModule(moduleMap, module) + + ProgressManager.getInstance().run( + object : Task.Backgroundable(project, "genIntellijRuns", false) { + override fun run(indicator: ProgressIndicator) { + indicator.isIndeterminate = true + + val projectDir = project.guessProjectDir() ?: return + indicator.text = "Creating run configurations" + indicator.text2 = "Running Gradle task: '$task'" + runGradleTaskAndWait(project, projectDir.localFile.toPath()) { settings -> + settings.taskNames = listOf(task) + } + + cleanupGeneratedRuns(project, mainModule) + } + }, + ) + } + + private fun cleanupGeneratedRuns(project: Project, module: Module) { + invokeAndWait { + if (!module.isDisposed) { + NeoForgeRunManagerListener(module, true) + } + } + + project.guessProjectDir()?.let { dir -> + LocalFileSystem.getInstance().refreshFiles(listOf(dir), true, true, null) + } + } + + private fun findMainModule(moduleMap: Map, module: Module): Module { + return moduleMap[module.name + ".main"] ?: module + } +} + +class NeoForgeRunManagerListener(private val module: Module, hasData: Boolean) : RunManagerListener { + private val count = AtomicInteger(3) + private val disposable = Disposer.newDisposable() + + init { + Disposer.register(module, disposable) + module.project.messageBus.connect(disposable).subscribe(RunManagerListener.TOPIC, this) + // If we don't have a data run, don't wait for it + if (!hasData) { + count.decrementAndGet() + } + } + + override fun runConfigurationAdded(settings: RunnerAndConfigurationSettings) { + val config = settings.configuration as? ApplicationConfiguration ?: return + + val postFixes = arrayOf("runClient", "runServer", "runData") + if (postFixes.none { settings.name.endsWith(it) }) { + return + } + + config.isAllowRunningInParallel = false + config.setModule(module) + RunManager.getInstance(module.project).addConfiguration(settings) + + if (count.decrementAndGet() == 0) { + Disposer.dispose(disposable) + } + } +} Index: src/main/kotlin/platform/neoforge/insight/NeoForgeImplicitUsageProvider.kt =================================================================== --- src/main/kotlin/platform/neoforge/insight/NeoForgeImplicitUsageProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/insight/NeoForgeImplicitUsageProvider.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,44 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.insight + +import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants +import com.demonwav.mcdev.util.extendsOrImplements +import com.intellij.codeInsight.daemon.ImplicitUsageProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod + +class NeoForgeImplicitUsageProvider : ImplicitUsageProvider { + + override fun isImplicitUsage(element: PsiElement) = isNetworkMessageOrHandler(element) + + private fun isNetworkMessageOrHandler(element: PsiElement): Boolean { + if (element !is PsiMethod || element.isConstructor && element.hasParameters()) { + return false + } + + val containingClass = element.containingClass ?: return false + return containingClass.extendsOrImplements(NeoForgeConstants.NETWORK_MESSAGE) + } + + override fun isImplicitRead(element: PsiElement) = false + override fun isImplicitWrite(element: PsiElement) = false +} Index: src/main/kotlin/platform/neoforge/util/NeoForgeConstants.kt =================================================================== --- src/main/kotlin/platform/neoforge/util/NeoForgeConstants.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/util/NeoForgeConstants.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.util + +object NeoForgeConstants { + + const val MOD_ANNOTATION = "net.neoforged.fml.common.Mod" + const val SUBSCRIBE_EVENT = "net.neoforged.bus.api.SubscribeEvent" + const val EVENT_BUS_SUBSCRIBER = "net.neoforged.fml.common.Mod.EventBusSubscriber" + const val FML_EVENT = "net.neoforged.fml.event.IModBusEvent" + const val EVENTBUS_EVENT = "net.neoforged.bus.api.Event" + const val NETWORK_MESSAGE = "net.neoforged.neoforge.network.simple.SimpleMessage" + const val MCMOD_INFO = "mcmod.info" + const val PACK_MCMETA = "pack.mcmeta" +} Index: src/main/kotlin/platform/neoforge/version/NeoForgeVersion.kt =================================================================== --- src/main/kotlin/platform/neoforge/version/NeoForgeVersion.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/kotlin/platform/neoforge/version/NeoForgeVersion.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,75 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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 . + */ + +package com.demonwav.mcdev.platform.neoforge.version + +import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.openapi.diagnostic.logger +import java.io.IOException + +class NeoForgeVersion private constructor(val versions: List) { + + val sortedMcVersions: List by lazy { + val version = versions.asSequence() + .map { it.substringBeforeLast('.') } + .distinct() + .mapNotNull { + val shortVersion = SemanticVersion.tryParse(it) + if (shortVersion != null) { + val parts = shortVersion.parts.toMutableList() + // Insert the '1.' part to the base neoforge version + parts.add(0, SemanticVersion.Companion.VersionPart.ReleasePart(1, "1")) + SemanticVersion(parts) + } else null + }.distinct() + .sortedDescending() + .toList() + return@lazy version + } + + fun getNeoForgeVersions(mcVersion: SemanticVersion): List { + val versionText = mcVersion.toString() + // Drop the 1. part of the mc version + val shortMcVersion = versionText.substringAfter('.') + val toList = versions.asSequence() + .filter { it.substringBeforeLast('.') == shortMcVersion } + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + .take(50) + .toList() + return toList + } + + companion object { + private val LOGGER = logger() + + suspend fun downloadData(): NeoForgeVersion? { + try { + val url = "https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml" + val versions = collectMavenVersions(url) + return NeoForgeVersion(versions) + } catch (e: IOException) { + LOGGER.error("Failed to retrieve NeoForge version data", e) + } + return null + } + } +} Index: src/main/kotlin/util/MinecraftTemplates.kt =================================================================== --- src/main/kotlin/util/MinecraftTemplates.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/util/MinecraftTemplates.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -111,6 +111,24 @@ mixinGroup.addTemplate(FileTemplateDescriptor(MIXIN_OVERWRITE_FALLBACK)) } + FileTemplateGroupDescriptor("NeoForge", PlatformAssets.NEOFORGE_ICON).let { forgeGroup -> + group.addTemplate(forgeGroup) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_MIXINS_JSON_TEMPLATE, PlatformAssets.NEOFORGE_ICON)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_MAIN_CLASS_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_CONFIG_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_BUILD_GRADLE_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_GRADLE_PROPERTIES_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_SETTINGS_GRADLE_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_MODS_TOML_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_PACK_MCMETA_TEMPLATE)) + + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_BLOCK_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_ITEM_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_PACKET_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_ENCHANTMENT_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(NEOFORGE_MOB_EFFECT_TEMPLATE)) + } + FileTemplateGroupDescriptor("Common", PlatformAssets.MINECRAFT_ICON).let { commonGroup -> group.addTemplate(commonGroup) commonGroup.addTemplate(FileTemplateDescriptor(GRADLE_GITIGNORE_TEMPLATE)) @@ -249,6 +267,21 @@ const val FABRIC_ITEM_TEMPLATE = "FabricItem.java" const val FABRIC_ENCHANTMENT_TEMPLATE = "FabricEnchantment.java" const val FABRIC_STATUS_EFFECT_TEMPLATE = "FabricStatusEffect.java" + + const val NEOFORGE_MIXINS_JSON_TEMPLATE = "NeoForge Mixins Config.json" + const val NEOFORGE_MAIN_CLASS_TEMPLATE = "NeoForge Main Class.java" + const val NEOFORGE_CONFIG_TEMPLATE = "NeoForge Config.java" + const val NEOFORGE_BUILD_GRADLE_TEMPLATE = "NeoForge build.gradle" + const val NEOFORGE_GRADLE_PROPERTIES_TEMPLATE = "NeoForge gradle.properties" + const val NEOFORGE_SETTINGS_GRADLE_TEMPLATE = "NeoForge settings.gradle" + const val NEOFORGE_MODS_TOML_TEMPLATE = "NeoForge mods.toml" + const val NEOFORGE_PACK_MCMETA_TEMPLATE = "NeoForge pack.mcmeta" + + const val NEOFORGE_BLOCK_TEMPLATE = "NeoForgeBlock.java" + const val NEOFORGE_ITEM_TEMPLATE = "NeoForgeItem.java" + const val NEOFORGE_PACKET_TEMPLATE = "NeoForgePacket.java" + const val NEOFORGE_ENCHANTMENT_TEMPLATE = "NeoForgeEnchantment.java" + const val NEOFORGE_MOB_EFFECT_TEMPLATE = "NeoForgeMobEffect.java" } private fun template(fileName: String, displayName: String? = null) = CustomDescriptor(fileName, displayName) Index: src/main/kotlin/util/MinecraftVersions.kt =================================================================== --- src/main/kotlin/util/MinecraftVersions.kt (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/kotlin/util/MinecraftVersions.kt (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -36,6 +36,8 @@ val MC1_19_4 = SemanticVersion.release(1, 19, 4) val MC1_20 = SemanticVersion.release(1, 20) val MC1_20_2 = SemanticVersion.release(1, 20, 2) + val MC1_20_3 = SemanticVersion.release(1, 20, 3) + val MC1_20_4 = SemanticVersion.release(1, 20, 4) fun requiredJavaVersion(minecraftVersion: SemanticVersion) = when { minecraftVersion <= MC1_16_5 -> JavaSdkVersion.JDK_1_8 Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/resources/META-INF/plugin.xml (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -41,6 +41,7 @@
  • Paper
  • Sponge
  • Forge
  • +
  • NeoForge
  • Fabric
  • Architectury
  • Velocity
  • @@ -88,6 +89,7 @@ + @@ -108,6 +110,7 @@ + @@ -167,6 +170,7 @@ * Bukkit * Sponge * Forge + * NeoForge * Fabric * MCP * Mixin @@ -349,6 +353,18 @@ + + + + + + + + + + + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,63 @@ +package ${PACKAGE_NAME}; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.neoforge.common.ModConfigSpec; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +// An example config class. This is not required, but it's a good idea to have one to keep your config organized. +// Demonstrates how to use Forge's config APIs +@Mod.EventBusSubscriber(modid = ${CLASS_NAME}.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class Config +{ + private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); + + private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER + .comment("Whether to log the dirt block on common setup") + .define("logDirtBlock", true); + + private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER + .comment("A magic number") + .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER + .comment("What you want the introduction message to be for the magic number") + .define("magicNumberIntroduction", "The magic number is... "); + + // a list of strings that are treated as resource locations for items + private static final ModConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER + .comment("A list of items to log on common setup.") + .defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName); + + static final ModConfigSpec SPEC = BUILDER.build(); + + public static boolean logDirtBlock; + public static int magicNumber; + public static String magicNumberIntroduction; + public static Set items; + + private static boolean validateItemName(final Object obj) + { + return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(new ResourceLocation(itemName)); + } + + @SubscribeEvent + static void onLoad(final ModConfigEvent event) + { + logDirtBlock = LOG_DIRT_BLOCK.get(); + magicNumber = MAGIC_NUMBER.get(); + magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get(); + + // convert the list of strings into a set of items + items = ITEM_STRINGS.get().stream() + .map(itemName -> BuiltInRegistries.ITEM.get(new ResourceLocation(itemName))) + .collect(Collectors.toSet()); + } +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Config.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new config class for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,129 @@ +package ${PACKAGE_NAME}; + +import com.mojang.logging.LogUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.material.MapColor; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.registries.DeferredBlock; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredItem; +import net.neoforged.neoforge.registries.DeferredRegister; +import org.slf4j.Logger; + +// The value here should match an entry in the META-INF/mods.toml file +@Mod(${CLASS_NAME}.MODID) +public class ${CLASS_NAME} +{ + // Define mod id in a common place for everything to reference + public static final String MODID = "${MOD_ID}"; + // Directly reference a slf4j logger + private static final Logger LOGGER = LogUtils.getLogger(); + // Create a Deferred Register to hold Blocks which will all be registered under the "${MOD_ID}" namespace + public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID); + // Create a Deferred Register to hold Items which will all be registered under the "${MOD_ID}" namespace + public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); + // Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "${MOD_ID}" namespace + public static final DeferredRegister CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); + + // Creates a new Block with the id "${MOD_ID}:example_block", combining the namespace and path + public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_block", BlockBehaviour.Properties.of().mapColor(MapColor.STONE)); + // Creates a new BlockItem with the id "${MOD_ID}:example_block", combining the namespace and path + public static final DeferredItem EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK); + + // Creates a new food item with the id "${MOD_ID}:example_id", nutrition 1 and saturation 2 + public static final DeferredItem EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder() + .alwaysEat().nutrition(1).saturationMod(2f).build())); + + // Creates a creative tab with the id "${MOD_ID}:example_tab" for the example item, that is placed after the combat tab + public static final DeferredHolder EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder() + .withTabsBefore(CreativeModeTabs.COMBAT) + .icon(() -> EXAMPLE_ITEM.get().getDefaultInstance()) + .displayItems((parameters, output) -> { + output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event + }).build()); + + // The constructor for the mod class is the first code that is run when your mod is loaded. + // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. + public ${CLASS_NAME}(IEventBus modEventBus) + { + // Register the commonSetup method for modloading + modEventBus.addListener(this::commonSetup); + + // Register the Deferred Register to the mod event bus so blocks get registered + BLOCKS.register(modEventBus); + // Register the Deferred Register to the mod event bus so items get registered + ITEMS.register(modEventBus); + // Register the Deferred Register to the mod event bus so tabs get registered + CREATIVE_MODE_TABS.register(modEventBus); + + // Register ourselves for server and other game events we are interested in + NeoForge.EVENT_BUS.register(this); + + // Register the item to a creative tab + modEventBus.addListener(this::addCreative); + + // Register our mod's ModConfigSpec so that FML can create and load the config file for us + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC); + } + + private void commonSetup(final FMLCommonSetupEvent event) + { + // Some common setup code + LOGGER.info("HELLO FROM COMMON SETUP"); + + if (Config.logDirtBlock) + LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); + + LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber); + + Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString())); + } + + // Add the example block item to the building blocks tab + private void addCreative(BuildCreativeModeTabContentsEvent event) + { + if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) + event.accept(EXAMPLE_BLOCK_ITEM); + } + + // You can use SubscribeEvent and let the Event Bus discover methods to call + @SubscribeEvent + public void onServerStarting(ServerStartingEvent event) + { + // Do something when the server starts + LOGGER.info("HELLO from server starting"); + } + + // You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent + @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) + public static class ClientModEvents + { + @SubscribeEvent + public static void onClientSetup(FMLClientSetupEvent event) + { + // Some client setup code + LOGGER.info("HELLO FROM CLIENT SETUP"); + LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); + } + } +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Main Class.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new main class for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "${PACKAGE_NAME}", + "compatibilityLevel": "JAVA_8", + "refmap": "${MOD_ID}.refmap.json", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge Mixins Config.json.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new modid.mixins.json file for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,141 @@ +plugins { + id 'java-library' + id 'eclipse' + id 'idea' + id 'maven-publish' + id 'net.neoforged.gradle.userdev' version '7.0.74' +} + +version = mod_version +group = mod_group_id + +repositories { + mavenLocal() +} + +base { + archivesName = mod_id +} + +java.toolchain.languageVersion = JavaLanguageVersion.of(${JAVA_VERSION}) + +//minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') +//minecraft.accessTransformers.entry public net.minecraft.client.Minecraft textureManager # textureManager + +// Default run configurations. +// These can be tweaked, removed, or duplicated as needed. +runs { + // applies to all the run configs below + configureEach { + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + systemProperty 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + systemProperty 'forge.logging.console.level', 'debug' + + modSource project.sourceSets.main + } + + client { + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id + } + + server { + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id + programArgument '--nogui' + } + + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id + } + + data { + // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it + // workingDirectory project.file('run-data') + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + } +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + + +dependencies { + // Specify the version of Minecraft to use. + // Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above. + // The group for userdev is net.neoforge, the module name is neoforge, and the version is the same as the neoforge version. + // You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader. + // And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version. + // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. + implementation "net.neoforged:neoforge:${neo_version}" + + // Example mod dependency with JEI + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" + // compileOnly "mezz.jei:jei-${mc_version}-forge-api:${jei_version}" + // runtimeOnly "mezz.jei:jei-${mc_version}-forge:${jei_version}" + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation "blank:coolmod-${mc_version}:${coolmod_version}" + + // Example mod dependency using a file as dependency + // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") + + // Example project dependency using a sister or child project: + // implementation project(":myproject") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html +} + +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. +// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html +tasks.withType(ProcessResources).configureEach { + var replaceProperties = [ + minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range, + neo_version : neo_version, neo_version_range: neo_version_range, + loader_version_range: loader_version_range, + mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, + mod_authors : mod_authors, mod_description: mod_description, + ] + inputs.properties replaceProperties + + filesMatching(['META-INF/mods.toml']) { + expand replaceProperties + [project: project] + } +} + +// Example configuration to allow publishing using the maven-publish plugin +publishing { + publications { + register('mavenJava', MavenPublication) { + from components.java + } + } + repositories { + maven { + url "file://${project.projectDir}/repo" + } + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge build.gradle.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new build.gradle for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,48 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx2G +org.gradle.daemon=false +org.gradle.debug=false + +#[[##]]# Environment Properties +# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge +# The Minecraft version must agree with the Neo version to get a valid artifact +minecraft_version=${MC_VERSION} +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[${MC_VERSION},${MC_NEXT_VERSION}) +# The Neo version must agree with the Minecraft version to get a valid artifact +neo_version=${NEOFORGE_VERSION} +# The Neo version range can use any version of Neo as bounds +neo_version_range=[${NEOFORGE_SPEC_VERSION},) +# The loader version range can only use the major version of FML as bounds +loader_version_range=[2,) + +#if (${PARCHMENT_VERSION}) +neogradle.subsystems.parchment.minecraftVersion=${PARCHMENT_MC_VERSION} +neogradle.subsystems.parchment.mappingsVersion=${PARCHMENT_VERSION} +#else +# Uncomment this to activate parchment +#neogradle.subsystems.parchment.minecraftVersion=${MC_VERSION} +#neogradle.subsystems.parchment.mappingsVersion=SET_ME +#end + +#[[##]]# Mod Properties + +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=${MOD_ID} +# The human-readable display name for the mod. +mod_name=${MOD_NAME} +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=${LICENSE} +# The mod version. See https://semver.org/ +mod_version=${MOD_VERSION} +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=${GROUP_ID} +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors=${AUTHOR_LIST} +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description=${DESCRIPTION} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge gradle.properties.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new gradle.properties file for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,87 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory +# A version range to match for said mod loader - for regular FML @Mod it will be the the FML version. This is currently 47. +loaderVersion="${loader_version_range}" #mandatory +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="${mod_license}" +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId="${mod_id}" #mandatory +# The version number of the mod +version="${mod_version}" #mandatory +# A display name for the mod +displayName="${mod_name}" #mandatory +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforge.net/docs/misc/updatechecker/ +#if (${UPDATE_URL}) +updateJSONURL="${UPDATE_URL}" #optional +#else +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional +#end +# A URL for the "homepage" for this mod, displayed in the mod UI +#if (${WEBSITE}) +displayURL="${WEBSITE}" #optional +#else +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional +#end +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="${MOD_ID}.png" #optional +# A text field displayed in the mod UI +#credits="" #optional +# A text field displayed in the mod UI +authors="${mod_authors}" #optional +# Display Test controls the display for your mod in the server connection screen +# MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. +# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. +# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. +# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. +# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. +#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) + +#if (${MIXIN_CONFIG}) +[[mixins]] +config="${MIXIN_CONFIG}" +#end + +# The description text for the mod (multi line!) (#mandatory) +description='''${mod_description}''' +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies."${mod_id}"]] #optional +# the modid of the dependency +modId="neoforge" #mandatory +# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). +# 'required' requires the mod to exist, 'optional' does not +# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning +type="required" #mandatory +# Optional field describing why the dependency is required or why it is incompatible +# reason="..." +# The version range of the dependency +versionRange="${neo_version_range}" #mandatory +# An ordering relationship for the dependency. +# BEFORE - This mod is loaded BEFORE the dependency +# AFTER - This mod is loaded AFTER the dependency +ordering="NONE" +# Side this dependency is applied on - BOTH, CLIENT, or SERVER +side="BOTH" +# Here's another dependency +[[dependencies."${mod_id}"]] +modId="minecraft" +type="required" +# This version range declares a minimum of the current minecraft version up to but not including the next major version +versionRange="${minecraft_version_range}" +ordering="NONE" +side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features."${mod_id}"] +#openGLVersion="[3.2,)" Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge mods.toml.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new mods.toml for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,11 @@ +{ + "pack": { + "description": "${MOD_ID} resources", + #if (${PACK_COMMENT} != "") + "pack_format": ${PACK_FORMAT}, + "_comment": "${PACK_COMMENT}" + #else + "pack_format": ${PACK_FORMAT} + #end + } +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge pack.mcmeta.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new pack.mcmeta for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,11 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = 'https://maven.neoforged.net/releases' } + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' +} Index: src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.html =================================================================== --- src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/neoforge/NeoForge settings.gradle.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,25 @@ + + + + +

    This is a built-in file template used to create a new settings.gradle for NeoForge projects.

    + + Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,10 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#parse("File Header.java") + +import net.minecraft.world.level.block.Block; + +public class ${NAME} extends Block { + public ${NAME}(Properties properties) { + super(properties); + } +} Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeBlock.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,21 @@ + + +A basic block class for NeoForge. Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,12 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#parse("File Header.java") + +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.EnchantmentCategory; + +public class ${NAME} extends Enchantment { + public ${NAME}(Rarity rarity, EnchantmentCategory category, EquipmentSlot[] slots) { + super(rarity, category, slots); + } +} Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeEnchantment.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,21 @@ + + +NeoForge style enchantment class Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,10 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#parse("File Header.java") + +import net.minecraft.world.item.Item; + +public class ${NAME} extends Item { + public ${NAME}(Properties properties) { + super(properties); + } +} Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeItem.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,21 @@ + + +An empty NeoForge item class. Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeMobEffect.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeMobEffect.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeMobEffect.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,11 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#parse("File Header.java") + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; + +public class ${NAME} extends MobEffect { + public ${NAME}(MobEffectCategory category, int color) { + super(category, color); + } +} Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeModEffect.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeModEffect.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgeModEffect.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,21 @@ + + +NeoForge style mob effect class Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.ft =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.ft (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,28 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#parse("File Header.java") + +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.neoforge.network.NetworkEvent; +import java.util.function.Supplier; + +public class ${NAME} { + + public ${NAME}() { + + } + + public ${NAME}(FriendlyByteBuf buf) { + + } + + public void toBytes(FriendlyByteBuf buf) { + + } + + public void handle(Supplier ctx) { + ctx.get().enqueueWork(() -> { + + }); + ctx.get().setPacketHandled(true); + } +} Index: src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.html =================================================================== --- src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) +++ src/main/resources/fileTemplates/j2ee/skeleton/neoforge/NeoForgePacket.java.html (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -0,0 +1,21 @@ + + +NeoForge style packet class Index: src/main/resources/messages/MinecraftDevelopment.properties =================================================================== --- src/main/resources/messages/MinecraftDevelopment.properties (revision 47053d6936a459a88ae0973f04b49b3965015c2b) +++ src/main/resources/messages/MinecraftDevelopment.properties (revision e23ddbd3b83a43380a29b1e50c3ae9bcae966255) @@ -46,6 +46,11 @@ creator.ui.depend.label=Depend: creator.ui.soft_depend.label=Soft Depend: creator.ui.mixins.label=Use Mixins: +creator.ui.parchment.label=Parchment: +creator.ui.parchment.include.label=Include: +creator.ui.parchment.include.old_mc.label=Older Minecraft versions +creator.ui.parchment.include.snapshots.label=Snapshot versions +creator.ui.parchment.no_version.message=No versions of Parchment matching your configuration creator.ui.outdated.message=Is the Minecraft project wizard outdated? \
    Create an issue on the MinecraftDev issue tracker.