From b0a4c621b656eea914e0943d7c204328a0f71563 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 30 Jul 2021 10:06:33 +0200 Subject: [PATCH 1/7] Fix #1043 --- .../velocity/framework/VelocityPresentationProvider.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/platform/velocity/framework/VelocityPresentationProvider.kt b/src/main/kotlin/platform/velocity/framework/VelocityPresentationProvider.kt index ed06b5b35..498650cf4 100644 --- a/src/main/kotlin/platform/velocity/framework/VelocityPresentationProvider.kt +++ b/src/main/kotlin/platform/velocity/framework/VelocityPresentationProvider.kt @@ -16,7 +16,6 @@ import com.intellij.framework.library.LibraryVersionProperties import com.intellij.openapi.roots.libraries.LibraryPresentationProvider import com.intellij.openapi.vfs.VirtualFile import java.io.BufferedReader -import java.io.IOException import java.util.jar.JarFile class VelocityPresentationProvider : LibraryPresentationProvider(VELOCITY_LIBRARY_KIND) { @@ -24,18 +23,17 @@ class VelocityPresentationProvider : LibraryPresentationProvider): LibraryVersionProperties? { for (classesRoot in classesRoots) { - try { + runCatching { // Velocity API jar has no Manifest entries, so we search for their annotation processor instead val registeredAPs = JarFile(classesRoot.localFile).use { jar -> val aps = jar.getEntry("META-INF/services/javax.annotation.processing.Processor") ?: return@use null jar.getInputStream(aps).bufferedReader().use(BufferedReader::readLines) - } ?: continue + } ?: return@runCatching if (registeredAPs.contains("com.velocitypowered.api.plugin.ap.PluginAnnotationProcessor")) { return LibraryVersionProperties() } - } catch (ignored: IOException) { } } return null From 60919926d87c849bf061c78774393cadc007c824 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 30 Jul 2021 12:37:54 +0200 Subject: [PATCH 2/7] Forge 1.17.1 templates Includes changes from MinecraftForge/MinecraftForge@22d609ed6c32d7075d4988cdcf77b0cf045de3e0 and MinecraftForge/MinecraftForge@be5446b91c17a3dbed880028741c430610bd59f1 Unfortunately the wizard is not aware of the official mappings channel, it'll be fixed later. --- .../platform/forge/creator/Fg3Template.kt | 28 ++++++- .../forge/creator/ForgeProjectConfig.kt | 2 +- .../forge/creator/ForgeProjectCreator.kt | 9 ++- .../creator/ForgeProjectSettingsWizard.kt | 10 ++- src/main/kotlin/util/MinecraftTemplates.kt | 2 + .../Forge (1.13+) Submodule build.gradle.ft | 60 +++++++++------ .../j2ee/forge/Forge (1.13+) build.gradle.ft | 56 ++++++++------ .../forge/Forge (1.17+) Main Class.java.ft | 74 +++++++++++++++++++ .../forge/Forge (1.17+) Main Class.java.html | 15 ++++ 9 files changed, 200 insertions(+), 56 deletions(-) create mode 100644 src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.ft create mode 100644 src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.html diff --git a/src/main/kotlin/platform/forge/creator/Fg3Template.kt b/src/main/kotlin/platform/forge/creator/Fg3Template.kt index 893aa0ba7..887dc8bbe 100644 --- a/src/main/kotlin/platform/forge/creator/Fg3Template.kt +++ b/src/main/kotlin/platform/forge/creator/Fg3Template.kt @@ -13,6 +13,7 @@ package com.demonwav.mcdev.platform.forge.creator import com.demonwav.mcdev.creator.buildsystem.BuildSystem import com.demonwav.mcdev.platform.BaseTemplate import com.demonwav.mcdev.platform.forge.util.ForgePackDescriptor +import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FG3_1_17_MAIN_CLASS_TEMPLATE import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FG3_BUILD_GRADLE_TEMPLATE import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FG3_GRADLE_PROPERTIES_TEMPLATE import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FG3_MAIN_CLASS_TEMPLATE @@ -21,6 +22,7 @@ import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FG3_SUBMODULE_BUILD_ import com.demonwav.mcdev.util.MinecraftTemplates.Companion.FORGE_MIXINS_JSON_TEMPLATE import com.demonwav.mcdev.util.MinecraftTemplates.Companion.MODS_TOML_TEMPLATE import com.demonwav.mcdev.util.MinecraftTemplates.Companion.PACK_MCMETA_TEMPLATE +import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.toPackageName import com.intellij.openapi.project.Project @@ -45,6 +47,24 @@ object Fg3Template : BaseTemplate() { return project.applyTemplate(FG3_MAIN_CLASS_TEMPLATE, props) } + fun apply1_17MainClass( + project: Project, + buildSystem: BuildSystem, + config: ForgeProjectConfig, + packageName: String, + className: String + ): String { + val props = mapOf( + "PACKAGE_NAME" to packageName, + "CLASS_NAME" to className, + "ARTIFACT_ID" to buildSystem.artifactId, + "MOD_NAME" to config.pluginName, + "MOD_VERSION" to buildSystem.version + ) + + return project.applyTemplate(FG3_1_17_MAIN_CLASS_TEMPLATE, props) + } + fun applyBuildGradle( project: Project, buildSystem: BuildSystem, @@ -53,7 +73,7 @@ object Fg3Template : BaseTemplate() { hasData: Boolean ): String { val (channel, version) = config.mcpVersion.mcpVersion.split('_', limit = 2) - val props = mutableMapOf( + val props = mutableMapOf( "MOD_NAME" to modName, "MCP_CHANNEL" to channel, "MCP_VERSION" to version, @@ -61,7 +81,8 @@ object Fg3Template : BaseTemplate() { "FORGE_VERSION" to config.forgeVersionText, "GROUP_ID" to buildSystem.groupId, "ARTIFACT_ID" to buildSystem.artifactId, - "MOD_VERSION" to buildSystem.version + "MOD_VERSION" to buildSystem.version, + "JAVA_VERSION" to if (config.mcVersion < MinecraftVersions.MC1_17) 8 else 16 ) if (hasData) { props["HAS_DATA"] = "true" @@ -96,13 +117,14 @@ object Fg3Template : BaseTemplate() { hasData: Boolean ): String { val (channel, version) = config.mcpVersion.mcpVersion.split('_', limit = 2) - val props = mutableMapOf( + val props = mutableMapOf( "MOD_NAME" to modName, "MCP_CHANNEL" to channel, "MCP_VERSION" to version, "MCP_MC_VERSION" to config.mcpVersion.mcVersion.toString(), "FORGE_VERSION" to config.forgeVersionText, "ARTIFACT_ID" to buildSystem.artifactId, + "JAVA_VERSION" to if (config.mcVersion < MinecraftVersions.MC1_17) 8 else 16, "COMMON_PROJECT_NAME" to buildSystem.commonModuleName ) if (hasData) { diff --git a/src/main/kotlin/platform/forge/creator/ForgeProjectConfig.kt b/src/main/kotlin/platform/forge/creator/ForgeProjectConfig.kt index 03206ba5d..1080a1e6d 100644 --- a/src/main/kotlin/platform/forge/creator/ForgeProjectConfig.kt +++ b/src/main/kotlin/platform/forge/creator/ForgeProjectConfig.kt @@ -61,7 +61,7 @@ class ForgeProjectConfig : ProjectConfig(), GradleCreator { buildSystem: GradleBuildSystem ) { buildSystem.gradleVersion = if (isFg3(mcVersion, forgeVersion)) { - Fg3ProjectCreator.FG4_WRAPPER_VERSION + Fg3ProjectCreator.FG5_WRAPPER_VERSION } else { Fg2ProjectCreator.FG_WRAPPER_VERSION } diff --git a/src/main/kotlin/platform/forge/creator/ForgeProjectCreator.kt b/src/main/kotlin/platform/forge/creator/ForgeProjectCreator.kt index b14cc0a40..ff8ea4657 100644 --- a/src/main/kotlin/platform/forge/creator/ForgeProjectCreator.kt +++ b/src/main/kotlin/platform/forge/creator/ForgeProjectCreator.kt @@ -23,6 +23,7 @@ import com.demonwav.mcdev.creator.buildsystem.gradle.GradleWrapperStep import com.demonwav.mcdev.creator.buildsystem.gradle.SimpleGradleSetupStep import com.demonwav.mcdev.platform.forge.util.ForgeConstants import com.demonwav.mcdev.platform.forge.util.ForgePackDescriptor +import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.runGradleTaskAndWait import com.demonwav.mcdev.util.runWriteTask @@ -105,7 +106,11 @@ open class Fg3ProjectCreator( private fun setupMainClassStep(): BasicJavaClassStep { return createJavaClassStep(config.mainClass) { packageName, className -> - Fg3Template.applyMainClass(project, buildSystem, config, packageName, className) + if (config.mcVersion >= MinecraftVersions.MC1_17) { + Fg3Template.apply1_17MainClass(project, buildSystem, config, packageName, className) + } else { + Fg3Template.applyMainClass(project, buildSystem, config, packageName, className) + } } } @@ -175,7 +180,7 @@ open class Fg3ProjectCreator( } companion object { - val FG4_WRAPPER_VERSION = SemanticVersion.release(6, 8, 1) + val FG5_WRAPPER_VERSION = SemanticVersion.release(7, 1, 1) } } diff --git a/src/main/kotlin/platform/forge/creator/ForgeProjectSettingsWizard.kt b/src/main/kotlin/platform/forge/creator/ForgeProjectSettingsWizard.kt index d93a15536..acd7eca11 100644 --- a/src/main/kotlin/platform/forge/creator/ForgeProjectSettingsWizard.kt +++ b/src/main/kotlin/platform/forge/creator/ForgeProjectSettingsWizard.kt @@ -19,9 +19,11 @@ import com.demonwav.mcdev.creator.ValidatedFieldType.LIST import com.demonwav.mcdev.creator.ValidatedFieldType.NON_BLANK import com.demonwav.mcdev.platform.PlatformType import com.demonwav.mcdev.platform.forge.version.ForgeVersion +import com.demonwav.mcdev.platform.mcp.McpVersionPair import com.demonwav.mcdev.platform.mcp.version.McpVersion import com.demonwav.mcdev.platform.mcp.version.McpVersionEntry import com.demonwav.mcdev.util.License +import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.modUpdateStep import com.intellij.ui.CollectionComboBoxModel @@ -171,7 +173,12 @@ class ForgeProjectSettingsWizard(private val creator: MinecraftProjectCreator) : conf.setAuthors(this.authorsField.text) conf.updateUrl = this.updateUrlField.text - conf.mcpVersion = (this.mcpVersionBox.selectedItem as McpVersionEntry).versionPair + conf.mcVersion = this.version ?: SemanticVersion.release() + conf.mcpVersion = if (conf.mcVersion >= MinecraftVersions.MC1_17) { + McpVersionPair("official_" + conf.mcVersion, conf.mcVersion) + } else { + (this.mcpVersionBox.selectedItem as McpVersionEntry).versionPair + } (this.forgeVersionBox.selectedItem as SemanticVersion).let { version -> val versionString = version.toString() @@ -182,7 +189,6 @@ class ForgeProjectSettingsWizard(private val creator: MinecraftProjectCreator) : conf.mixins = mixinsCheckbox.isSelected conf.license = licenseBox.selectedItem as? License ?: License.ALL_RIGHTS_RESERVED - conf.mcVersion = this.version ?: SemanticVersion.release() } private fun mcVersionUpdate(data: Data) { diff --git a/src/main/kotlin/util/MinecraftTemplates.kt b/src/main/kotlin/util/MinecraftTemplates.kt index c0d4fb1d9..b1f4fd74c 100644 --- a/src/main/kotlin/util/MinecraftTemplates.kt +++ b/src/main/kotlin/util/MinecraftTemplates.kt @@ -88,6 +88,7 @@ class MinecraftTemplates : FileTemplateGroupDescriptorFactory { forgeGroup.addTemplate(FileTemplateDescriptor(FORGE_SETTINGS_GRADLE_TEMPLATE)) forgeGroup.addTemplate(FileTemplateDescriptor(FORGE_MIXINS_JSON_TEMPLATE, PlatformAssets.FORGE_ICON)) forgeGroup.addTemplate(FileTemplateDescriptor(FG3_MAIN_CLASS_TEMPLATE)) + forgeGroup.addTemplate(FileTemplateDescriptor(FG3_1_17_MAIN_CLASS_TEMPLATE)) forgeGroup.addTemplate(FileTemplateDescriptor(FG3_BUILD_GRADLE_TEMPLATE)) forgeGroup.addTemplate(FileTemplateDescriptor(FG3_SUBMODULE_BUILD_GRADLE_TEMPLATE)) forgeGroup.addTemplate(FileTemplateDescriptor(FG3_GRADLE_PROPERTIES_TEMPLATE)) @@ -221,6 +222,7 @@ class MinecraftTemplates : FileTemplateGroupDescriptorFactory { const val FORGE_MIXINS_JSON_TEMPLATE = "Forge Mixins Config.json" const val FORGE_SETTINGS_GRADLE_TEMPLATE = "Forge settings.gradle" const val FG3_MAIN_CLASS_TEMPLATE = "Forge (1.13+) Main Class.java" + const val FG3_1_17_MAIN_CLASS_TEMPLATE = "Forge (1.17+) Main Class.java" const val FG3_BUILD_GRADLE_TEMPLATE = "Forge (1.13+) build.gradle" const val FG3_SUBMODULE_BUILD_GRADLE_TEMPLATE = "Forge (1.13+) Submodule build.gradle" const val FG3_GRADLE_PROPERTIES_TEMPLATE = "Forge (1.13+) gradle.properties" diff --git a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) Submodule build.gradle.ft b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) Submodule build.gradle.ft index 14b090add..5ca650448 100644 --- a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) Submodule build.gradle.ft +++ b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) Submodule build.gradle.ft @@ -1,5 +1,6 @@ buildscript { repositories { + // These repositories are only for Gradle plugins, put any other repositories in the repository block further below maven { url = 'https://maven.minecraftforge.net' } #if (${MIXINS}) maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } @@ -7,7 +8,7 @@ buildscript { mavenCentral() } dependencies { - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '4.1.+', changing: true + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true #if (${MIXINS}) classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' #end @@ -21,11 +22,11 @@ apply plugin: 'org.spongepowered.mixin' java { archivesBaseName = '${ARTIFACT_ID}' - toolchain.languageVersion = JavaLanguageVersion.of(8) // Mojang ships Java 8 to end users, so your mod should target Java 8. + toolchain.languageVersion = JavaLanguageVersion.of(${JAVA_VERSION}) } minecraft { - // The mappings can be changed at any time, and must be in the following format. + // The mappings can be changed at any time and must be in the following format. // Channel: Version: // snapshot YYYYMMDD Snapshot are built nightly. // stable # Stables are built at the discretion of the MCP team. @@ -34,10 +35,13 @@ minecraft { // You must be aware of the Mojang license when using the 'official' mappings. // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md // - // Use non-default mappings at your own risk. they may not always work. + // Use non-default mappings at your own risk. They may not always work. // Simply re-run your setup task after changing the mappings to update your workspace. +#if(${MCP_CHANNEL} == "stable" || ${MCP_CHANNEL} == "snapshot") mappings channel: '${MCP_CHANNEL}', version: '${MCP_VERSION}-${MCP_MC_VERSION}' - // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. +#else + mappings channel: '${MCP_CHANNEL}', version: '${MCP_VERSION}' +#end // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') @@ -48,7 +52,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -74,7 +78,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -100,7 +104,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -133,27 +137,31 @@ mixin { // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } +repositories { + // Put repositories for dependencies here + // ForgeGradle automatically adds the Forge maven and Maven Central for you + + // If you have mod jar dependencies in ./libs, you can declare them as a repository like so: + // flatDir { + // dir 'libs' + // } +} + dependencies { - // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed - // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. + // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft' it is assumed + // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied. // The userdev artifact is a special name and will get all sorts of transformations applied to it. minecraft 'net.minecraftforge:forge:${FORGE_VERSION}' api project(":${COMMON_PROJECT_NAME}") - // You may put jars on which you depend on in ./libs or you may define them like so.. - // compile "some.group:artifact:version:classifier" - // compile "some.group:artifact:version" + // Real mod deobf dependency examples - these get remapped to your current mappings + // compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency + // runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency + // implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency - // Real examples - // compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env - // compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env - - // The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. - // provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' - - // These dependencies get remapped to your current MCP mappings - // deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev' + // Examples using mod jars from ./libs + // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html @@ -164,7 +172,7 @@ dependencies { #end } -// Example for how to get properties into the manifest for reading by the runtime.. +// Example for how to get properties into the manifest for reading at runtime. jar { manifest { attributes([ @@ -176,7 +184,7 @@ jar { #end "Specification-Version": "1", // We are version 1 of ourselves "Implementation-Title": project.name, - "Implementation-Version": project.version, + "Implementation-Version": project.jar.archiveVersion, #if (${AUTHOR_LIST}) "Implementation-Vendor": "${AUTHOR_LIST}", #else @@ -190,4 +198,8 @@ jar { } } +tasks.withType(JavaCompile).configureEach { + options.release = ${JAVA_VERSION} +} + jar.finalizedBy('reobfJar') diff --git a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) build.gradle.ft b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) build.gradle.ft index 378a4ea0d..e7f852b33 100644 --- a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) build.gradle.ft +++ b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.13+) build.gradle.ft @@ -1,5 +1,6 @@ buildscript { repositories { + // These repositories are only for Gradle plugins, put any other repositories in the repository block further below maven { url = 'https://maven.minecraftforge.net' } #if (${MIXINS}) maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } @@ -7,7 +8,7 @@ buildscript { mavenCentral() } dependencies { - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '4.1.+', changing: true + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true #if (${MIXINS}) classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' #end @@ -24,11 +25,11 @@ version = '${MOD_VERSION}' java { archivesBaseName = '${ARTIFACT_ID}' - toolchain.languageVersion = JavaLanguageVersion.of(8) // Mojang ships Java 8 to end users, so your mod should target Java 8. + toolchain.languageVersion = JavaLanguageVersion.of(${JAVA_VERSION}) } minecraft { - // The mappings can be changed at any time, and must be in the following format. + // The mappings can be changed at any time and must be in the following format. // Channel: Version: // snapshot YYYYMMDD Snapshot are built nightly. // stable # Stables are built at the discretion of the MCP team. @@ -37,10 +38,13 @@ minecraft { // You must be aware of the Mojang license when using the 'official' mappings. // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md // - // Use non-default mappings at your own risk. they may not always work. + // Use non-default mappings at your own risk. They may not always work. // Simply re-run your setup task after changing the mappings to update your workspace. +#if(${MCP_CHANNEL} == "stable" || ${MCP_CHANNEL} == "snapshot") mappings channel: '${MCP_CHANNEL}', version: '${MCP_VERSION}-${MCP_MC_VERSION}' - // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. +#else + mappings channel: '${MCP_CHANNEL}', version: '${MCP_VERSION}' +#end // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') @@ -51,7 +55,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -77,7 +81,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -104,7 +108,7 @@ minecraft { workingDirectory project.file('run') // Recommended logging data for a userdev environment - // The markers can be changed as needed. + // The markers can be added/removed as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. @@ -137,25 +141,29 @@ mixin { // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } +repositories { + // Put repositories for dependencies here + // ForgeGradle automatically adds the Forge maven and Maven Central for you + + // If you have mod jar dependencies in ./libs, you can declare them as a repository like so: + // flatDir { + // dir 'libs' + // } +} + dependencies { - // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed - // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. + // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft' it is assumed + // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied. // The userdev artifact is a special name and will get all sorts of transformations applied to it. minecraft 'net.minecraftforge:forge:${FORGE_VERSION}' - // You may put jars on which you depend on in ./libs or you may define them like so.. - // compile "some.group:artifact:version:classifier" - // compile "some.group:artifact:version" - - // Real examples - // compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env - // compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env - - // The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. - // provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' + // Real mod deobf dependency examples - these get remapped to your current mappings + // compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency + // runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency + // implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency - // These dependencies get remapped to your current MCP mappings - // deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev' + // Examples using mod jars from ./libs + // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html @@ -166,7 +174,7 @@ dependencies { #end } -// Example for how to get properties into the manifest for reading by the runtime.. +// Example for how to get properties into the manifest for reading at runtime. jar { manifest { attributes([ @@ -178,7 +186,7 @@ jar { #end "Specification-Version": "1", // We are version 1 of ourselves "Implementation-Title": project.name, - "Implementation-Version": project.version, + "Implementation-Version": project.jar.archiveVersion, #if (${AUTHOR_LIST}) "Implementation-Vendor": "${AUTHOR_LIST}", #else diff --git a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.ft b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.ft new file mode 100644 index 000000000..ed7e8ab02 --- /dev/null +++ b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.ft @@ -0,0 +1,74 @@ +package ${PACKAGE_NAME}; + +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.InterModComms; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; +import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; +import net.minecraftforge.fmlserverevents.FMLServerStartingEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.stream.Collectors; + +// The value here should match an entry in the META-INF/mods.toml file +@Mod("${ARTIFACT_ID}") +public class ${CLASS_NAME} { + + // Directly reference a log4j logger. + private static final Logger LOGGER = LogManager.getLogger(); + + public ${CLASS_NAME}() { + // Register the setup method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); + // Register the enqueueIMC method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC); + // Register the processIMC method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC); + + // Register ourselves for server and other game events we are interested in + MinecraftForge.EVENT_BUS.register(this); + } + + private void setup(final FMLCommonSetupEvent event) { + // some preinit code + LOGGER.info("HELLO FROM PREINIT"); + LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); + } + + private void enqueueIMC(final InterModEnqueueEvent event) { + // some example code to dispatch IMC to another mod + InterModComms.sendTo("${ARTIFACT_ID}", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";}); + } + + private void processIMC(final InterModProcessEvent event) { + // some example code to receive and process InterModComms from other mods + LOGGER.info("Got IMC {}", event.getIMCStream(). + map(m->m.messageSupplier().get()). + collect(Collectors.toList())); + } + + // You can use SubscribeEvent and let the Event Bus discover methods to call + @SubscribeEvent + public void onServerStarting(FMLServerStartingEvent event) { + // do something when the server starts + LOGGER.info("HELLO from server starting"); + } + + // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD + // Event bus for receiving Registry Events) + @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) + public static class RegistryEvents { + @SubscribeEvent + public static void onBlocksRegistry(final RegistryEvent.Register blockRegistryEvent) { + // register a new block here + LOGGER.info("HELLO from Register Block"); + } + } +} diff --git a/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.html b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.html new file mode 100644 index 000000000..f3a156a68 --- /dev/null +++ b/src/main/resources/fileTemplates/j2ee/forge/Forge (1.17+) Main Class.java.html @@ -0,0 +1,15 @@ + + + + +

This is a built-in file template used to create a new main class for Forge projects 1.17 and above

+ + From 0213f788c53bddff1d269760357f103149c1e6c3 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 3 Aug 2021 18:35:58 +0200 Subject: [PATCH 3/7] Fix SpongeAPI 8+ library detection Manifest title changed from SpongeAPI to spongeapi --- .../framework/SpongePresentationProvider.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/platform/sponge/framework/SpongePresentationProvider.kt b/src/main/kotlin/platform/sponge/framework/SpongePresentationProvider.kt index 1852cd770..73db3ca3c 100644 --- a/src/main/kotlin/platform/sponge/framework/SpongePresentationProvider.kt +++ b/src/main/kotlin/platform/sponge/framework/SpongePresentationProvider.kt @@ -26,17 +26,19 @@ class SpongePresentationProvider : LibraryPresentationProvider): LibraryVersionProperties? { - loop@ for (classesRoot in classesRoots) { + for (classesRoot in classesRoots) { val manifest = classesRoot.manifest ?: continue - val versionAttribute = when ("SpongeAPI") { - manifest[IMPLEMENTATION_TITLE] -> IMPLEMENTATION_VERSION - manifest[SPECIFICATION_TITLE] -> SPECIFICATION_VERSION - else -> continue@loop - } + loop@ for (title in setOf("SpongeAPI", "spongeapi")) { + val versionAttribute = when (title) { + manifest[IMPLEMENTATION_TITLE] -> IMPLEMENTATION_VERSION + manifest[SPECIFICATION_TITLE] -> SPECIFICATION_VERSION + else -> continue@loop + } - val version = manifest[versionAttribute] ?: continue - return LibraryVersionProperties(version) + val version = manifest[versionAttribute] ?: continue + return LibraryVersionProperties(version) + } } return null } From 21439d411cd53f2328847f5b8674a616f6cd6c3a Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 3 Aug 2021 19:03:57 +0200 Subject: [PATCH 4/7] Create GradleRunConfiguration directly Other plugins (like the Kotlin plugin) do not expect ExternalSystemRunConfiguration and throw errors --- .../creator/buildsystem/gradle/gradle-steps.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/creator/buildsystem/gradle/gradle-steps.kt b/src/main/kotlin/creator/buildsystem/gradle/gradle-steps.kt index cba326f03..869a2be65 100644 --- a/src/main/kotlin/creator/buildsystem/gradle/gradle-steps.kt +++ b/src/main/kotlin/creator/buildsystem/gradle/gradle-steps.kt @@ -25,7 +25,6 @@ import com.demonwav.mcdev.util.virtualFileOrError import com.intellij.codeInsight.actions.ReformatCodeProcessor import com.intellij.execution.RunManager import com.intellij.ide.ui.UISettings -import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration import com.intellij.openapi.module.Module import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.project.Project @@ -41,9 +40,9 @@ import java.nio.file.StandardOpenOption.CREATE import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING import java.nio.file.StandardOpenOption.WRITE import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType +import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration import org.jetbrains.plugins.gradle.service.project.open.canLinkAndRefreshGradleProject import org.jetbrains.plugins.gradle.service.project.open.linkAndRefreshGradleProject -import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.groovy.GroovyLanguage import org.jetbrains.plugins.groovy.lang.psi.GroovyFile import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory @@ -286,12 +285,7 @@ class BasicGradleFinalizerStep( val runManager = RunManager.getInstance(project) val runConfigName = buildSystem.artifactId + ' ' + task - val runConfiguration = ExternalSystemRunConfiguration( - GradleConstants.SYSTEM_ID, - project, - gradleType.configurationFactories[0], - runConfigName - ) + val runConfiguration = GradleRunConfiguration(project, gradleType.factory, runConfigName) // Set relevant gradle values runConfiguration.settings.externalProjectPath = rootDirectory.toAbsolutePath().toString() @@ -302,7 +296,7 @@ class BasicGradleFinalizerStep( val settings = runManager.createConfiguration( runConfiguration, - GradleExternalTaskConfigurationType.getInstance().configurationFactories.first() + gradleType.factory ) settings.isActivateToolWindowBeforeRun = true From 893b815558d3553b79d58a9615ff932b7d2d8caa Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 8 Aug 2021 18:23:05 +0200 Subject: [PATCH 5/7] Support alternative sided annotations Bonus: - fix a bug where a sided method in conflict with its class side did not produce an error - quick fix of the unused mixin inspection now takes mixed-in classes into account (and tells the user on which list it will add the mixin) --- .../FieldDeclarationSideOnlyInspection.kt | 33 ++-- ...alVariableDeclarationSideOnlyInspection.kt | 40 ++--- .../sideonly/MethodCallSideOnlyInspection.kt | 58 +++---- .../sideonly/MethodSideOnlyInspection.kt | 55 +++---- .../sideonly/NestedClassSideOnlyInspection.kt | 21 ++- .../NewExpressionSideOnlyInspection.kt | 37 ++--- .../RemoveAnnotationInspectionGadgetsFix.kt | 16 +- .../forge/inspections/sideonly/Side.kt | 10 +- .../inspections/sideonly/SideAnnotation.kt | 57 +++++++ .../inspections/sideonly/SideOnlyUtil.kt | 141 +++++++++--------- .../sideonly/VariableUseSideOnlyInspection.kt | 50 +++---- .../mixin/inspection/UnusedMixinInspection.kt | 18 ++- 12 files changed, 300 insertions(+), 236 deletions(-) create mode 100644 src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/FieldDeclarationSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/FieldDeclarationSideOnlyInspection.kt index 6440a0527..ee258aacb 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/FieldDeclarationSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/FieldDeclarationSideOnlyInspection.kt @@ -10,6 +10,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClassType import com.intellij.psi.PsiField import com.siyeh.ig.BaseInspection @@ -35,10 +36,10 @@ class FieldDeclarationSideOnlyInspection : BaseInspection() { } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { - val field = infos[3] as PsiField + val annotation = infos[3] as PsiAnnotation - return if (field.isWritable) { - RemoveAnnotationInspectionGadgetsFix(field, "Remove @SideOnly annotation from field") + return if (annotation.isWritable) { + RemoveAnnotationInspectionGadgetsFix(annotation, "Remove @SideOnly annotation from field") } else { null } @@ -53,24 +54,24 @@ class FieldDeclarationSideOnlyInspection : BaseInspection() { return } - val fieldSide = SideOnlyUtil.checkField(field) - if (fieldSide === Side.INVALID) { + val (fieldAnnotation, fieldSide) = SideOnlyUtil.checkField(field) + if (fieldAnnotation == null || fieldSide === Side.INVALID) { return } - val classSide = SideOnlyUtil.getSideForClass(psiClass) + val (classAnnotation, classSide) = SideOnlyUtil.getSideForClass(psiClass) if (fieldSide !== Side.NONE && fieldSide !== classSide) { - if (classSide !== Side.NONE && classSide !== Side.INVALID) { + if (classAnnotation != null && classSide !== Side.NONE && classSide !== Side.INVALID) { registerFieldError( field, Error.CLASS_CROSS_ANNOTATED, - fieldSide.annotation, - classSide.annotation, - field + fieldAnnotation.renderSide(fieldSide), + classAnnotation.renderSide(classSide), + field.getAnnotation(fieldAnnotation.annotationName) ) } else if (classSide !== Side.NONE) { - registerFieldError(field, Error.CLASS_UNANNOTATED, fieldSide.annotation, null, field) + registerFieldError(field, Error.CLASS_UNANNOTATED, fieldAnnotation, null, field) } } @@ -85,9 +86,9 @@ class FieldDeclarationSideOnlyInspection : BaseInspection() { val type = field.type as PsiClassType val fieldClass = type.resolve() ?: return - val fieldClassSide = SideOnlyUtil.getSideForClass(fieldClass) + val (fieldClassAnnotation, fieldClassSide) = SideOnlyUtil.getSideForClass(fieldClass) - if (fieldClassSide === Side.NONE || fieldClassSide === Side.INVALID) { + if (fieldClassAnnotation == null || fieldClassSide === Side.NONE || fieldClassSide === Side.INVALID) { return } @@ -95,9 +96,9 @@ class FieldDeclarationSideOnlyInspection : BaseInspection() { registerFieldError( field, Error.FIELD_CROSS_ANNOTATED, - fieldClassSide.annotation, - fieldSide.annotation, - field + fieldClassAnnotation.renderSide(fieldClassSide), + fieldAnnotation.renderSide(fieldSide), + field.getAnnotation(fieldAnnotation.annotationName) ) } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/LocalVariableDeclarationSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/LocalVariableDeclarationSideOnlyInspection.kt index fe2eee484..f07e382c9 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/LocalVariableDeclarationSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/LocalVariableDeclarationSideOnlyInspection.kt @@ -11,7 +11,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly import com.demonwav.mcdev.util.findContainingClass -import com.intellij.psi.PsiClass +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClassType import com.intellij.psi.PsiLocalVariable import com.siyeh.ig.BaseInspection @@ -34,11 +34,11 @@ class LocalVariableDeclarationSideOnlyInspection : BaseInspection() { " or method that does not match the same side." override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { - val variableClass = infos[3] as PsiClass + val annotation = infos[3] as PsiAnnotation - return if (variableClass.isWritable) { + return if (annotation.isWritable) { RemoveAnnotationInspectionGadgetsFix( - variableClass, + annotation, "Remove @SideOnly annotation from variable class declaration" ) } else { @@ -59,30 +59,32 @@ class LocalVariableDeclarationSideOnlyInspection : BaseInspection() { val variableClass = type.resolve() ?: return - val variableSide = SideOnlyUtil.getSideForClass(variableClass) - if (variableSide === Side.NONE || variableSide === Side.INVALID) { + val (variableAnnotation, variableSide) = SideOnlyUtil.getSideForClass(variableClass) + if (variableAnnotation == null || variableSide === Side.NONE || variableSide === Side.INVALID) { return } - val containingClassSide = SideOnlyUtil.getSideForClass(psiClass) - val methodSide = SideOnlyUtil.checkElementInMethod(variable) + val (containingClassAnnotation, containingClassSide) = SideOnlyUtil.getSideForClass(psiClass) + val (methodAnnotation, methodSide) = SideOnlyUtil.checkElementInMethod(variable) var classAnnotated = false - if (containingClassSide !== Side.NONE && containingClassSide !== Side.INVALID) { + if (containingClassAnnotation != null && + containingClassSide !== Side.NONE && containingClassSide !== Side.INVALID + ) { if (variableSide !== containingClassSide) { registerVariableError( variable, Error.VAR_CROSS_ANNOTATED_CLASS, - variableSide.annotation, - containingClassSide.annotation, - variableClass + variableAnnotation.renderSide(variableSide), + containingClassAnnotation.renderSide(containingClassSide), + variableClass.getAnnotation(variableAnnotation.annotationName) ) } classAnnotated = true } - if (methodSide === Side.INVALID) { + if (methodAnnotation == null || methodSide === Side.INVALID) { return } @@ -92,18 +94,18 @@ class LocalVariableDeclarationSideOnlyInspection : BaseInspection() { registerVariableError( variable, Error.VAR_UNANNOTATED_METHOD, - variableSide.annotation, - methodSide.annotation, - variableClass + variableAnnotation.renderSide(variableSide), + methodAnnotation.renderSide(methodSide), + variableClass.getAnnotation(variableAnnotation.annotationName) ) } } else { registerVariableError( variable, Error.VAR_CROSS_ANNOTATED_METHOD, - variableSide.annotation, - methodSide.annotation, - variableClass + variableAnnotation.renderSide(variableSide), + methodAnnotation.renderSide(methodSide), + variableClass.getAnnotation(variableAnnotation.annotationName) ) } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/MethodCallSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/MethodCallSideOnlyInspection.kt index 3eb06074a..4644f85cf 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/MethodCallSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/MethodCallSideOnlyInspection.kt @@ -12,6 +12,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly import com.demonwav.mcdev.platform.forge.util.ForgeConstants import com.demonwav.mcdev.util.findContainingClass +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethodCallExpression @@ -37,10 +38,10 @@ class MethodCallSideOnlyInspection : BaseInspection() { "used in matching @SideOnly classes and methods." override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { - val method = infos[3] as PsiMethod + val annotation = infos[3] as PsiAnnotation - return if (method.isWritable) { - RemoveAnnotationInspectionGadgetsFix(method, "Remove @SideOnly annotation from method declaration") + return if (annotation.isWritable) { + RemoveAnnotationInspectionGadgetsFix(annotation, "Remove @SideOnly annotation from method declaration") } else { null } @@ -75,50 +76,53 @@ class MethodCallSideOnlyInspection : BaseInspection() { val declaration = referenceExpression.resolve() as? PsiMethod ?: return - var elementSide = SideOnlyUtil.checkMethod(declaration) + var (elementAnnotation, elementSide) = SideOnlyUtil.checkMethod(declaration) // Check the class(es) the element is declared in val declarationContainingClass = declaration.containingClass ?: return val declarationClassHierarchySides = SideOnlyUtil.checkClassHierarchy(declarationContainingClass) - val declarationClassSide = SideOnlyUtil.getFirstSide(declarationClassHierarchySides) + val (declarationClassAnnotation, declarationClassSide) = + SideOnlyUtil.getFirstSide(declarationClassHierarchySides) // The element inherits the @SideOnly from it's parent class if it doesn't explicitly set it itself var inherited = false - if (declarationClassSide !== Side.NONE && (elementSide === Side.INVALID || elementSide === Side.NONE)) { + if (declarationClassAnnotation != null && + declarationClassSide !== Side.NONE && (elementSide === Side.INVALID || elementSide === Side.NONE) + ) { inherited = true elementSide = declarationClassSide } - if (elementSide === Side.INVALID || elementSide === Side.NONE) { + if (elementAnnotation == null || elementSide === Side.INVALID || elementSide === Side.NONE) { return } // Check the class(es) the element is in val containingClass = expression.findContainingClass() ?: return - val classSide = SideOnlyUtil.getSideForClass(containingClass) + val (classAnnotation, classSide) = SideOnlyUtil.getSideForClass(containingClass) var classAnnotated = false - if (classSide !== Side.NONE && classSide !== Side.INVALID) { + if (classAnnotation != null && classSide !== Side.NONE && classSide !== Side.INVALID) { if (classSide !== elementSide) { if (inherited) { registerError( referenceExpression.element, Error.ANNOTATED_CLASS_METHOD_IN_CROSS_ANNOTATED_CLASS_METHOD, - elementSide.annotation, - classSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + classAnnotation.renderSide(classSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( referenceExpression.element, Error.ANNOTATED_METHOD_IN_CROSS_ANNOTATED_CLASS_METHOD, - elementSide.annotation, - classSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + classAnnotation.renderSide(classSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } } @@ -126,10 +130,10 @@ class MethodCallSideOnlyInspection : BaseInspection() { } // Check the method the element is in - val methodSide = SideOnlyUtil.checkElementInMethod(expression) + val (methodAnnotation, methodSide) = SideOnlyUtil.checkElementInMethod(expression) // Put error on for method - if (elementSide !== methodSide && methodSide !== Side.INVALID) { + if (methodAnnotation != null && elementSide !== methodSide && methodSide !== Side.INVALID) { if (methodSide === Side.NONE) { // If the class is properly annotated the method doesn't need to also be annotated if (!classAnnotated) { @@ -137,17 +141,17 @@ class MethodCallSideOnlyInspection : BaseInspection() { registerError( referenceExpression.element, Error.ANNOTATED_CLASS_METHOD_IN_UNANNOTATED_METHOD, - elementSide.annotation, + elementAnnotation.renderSide(elementSide), null, - declaration + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( referenceExpression.element, Error.ANNOTATED_METHOD_IN_UNANNOTATED_METHOD, - elementSide.annotation, + elementAnnotation.renderSide(elementSide), null, - declaration + declaration.getAnnotation(elementAnnotation.annotationName) ) } } @@ -156,17 +160,17 @@ class MethodCallSideOnlyInspection : BaseInspection() { registerError( referenceExpression.element, Error.ANNOTATED_CLASS_METHOD_IN_CROSS_ANNOTATED_METHOD, - elementSide.annotation, - methodSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + methodAnnotation.renderSide(methodSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( referenceExpression.element, Error.ANNOTATED_METHOD_IN_CROSS_ANNOTATED_METHOD, - elementSide.annotation, - methodSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + methodAnnotation.renderSide(methodSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/MethodSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/MethodSideOnlyInspection.kt index 6476cf463..02f0cae0c 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/MethodSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/MethodSideOnlyInspection.kt @@ -10,6 +10,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClassType import com.intellij.psi.PsiMethod import com.siyeh.ig.BaseInspection @@ -36,10 +37,10 @@ class MethodSideOnlyInspection : BaseInspection() { override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val error = infos[0] as Error - val method = infos[3] as PsiMethod + val annotation = infos[3] as PsiAnnotation - return if (method.isWritable && error === Error.METHOD_IN_WRONG_CLASS) { - RemoveAnnotationInspectionGadgetsFix(method, "Remove @SideOnly annotation from method") + return if (annotation.isWritable && error === Error.METHOD_IN_WRONG_CLASS) { + RemoveAnnotationInspectionGadgetsFix(annotation, "Remove @SideOnly annotation from method") } else { null } @@ -54,54 +55,54 @@ class MethodSideOnlyInspection : BaseInspection() { return } - val methodSide = SideOnlyUtil.checkMethod(method) - - val returnType = method.returnType as? PsiClassType ?: return + val (methodAnnotation, methodSide) = SideOnlyUtil.checkMethod(method) + if (methodAnnotation == null) { + return + } - val resolve = returnType.resolve() ?: return + val resolve = (method.returnType as? PsiClassType)?.resolve() - val returnSide = SideOnlyUtil.getSideForClass(resolve) - if (returnSide !== Side.NONE && returnSide !== Side.INVALID && returnSide !== methodSide && - methodSide !== Side.NONE && methodSide !== Side.INVALID + val (returnAnnotation, returnSide) = + if (resolve == null) null to Side.NONE else SideOnlyUtil.getSideForClass(resolve) + if (returnAnnotation != null && returnSide !== Side.NONE && returnSide !== Side.INVALID && + returnSide !== methodSide && methodSide !== Side.NONE && methodSide !== Side.INVALID ) { registerMethodError( method, Error.RETURN_TYPE_ON_WRONG_METHOD, - methodSide.annotation, - returnSide.annotation, - method + methodAnnotation.renderSide(methodSide), + returnAnnotation.renderSide(returnSide), + method.getAnnotation(methodAnnotation.annotationName) ) } - val classHierarchySides = SideOnlyUtil.checkClassHierarchy(psiClass) - - for (classHierarchySide in classHierarchySides) { - if (classHierarchySide.first !== Side.NONE && classHierarchySide.first !== Side.INVALID) { + for ((classAnnotation, classSide) in SideOnlyUtil.checkClassHierarchy(psiClass)) { + if (classAnnotation != null && classSide !== Side.NONE && classSide !== Side.INVALID) { if ( - methodSide !== classHierarchySide.first && + methodSide !== classSide && methodSide !== Side.NONE && methodSide !== Side.INVALID ) { registerMethodError( method, Error.METHOD_IN_WRONG_CLASS, - methodSide.annotation, - classHierarchySide.first.annotation, - method + methodAnnotation.renderSide(methodSide), + classAnnotation.renderSide(classSide), + method.getAnnotation(methodAnnotation.annotationName) ) } - if (returnSide !== Side.NONE && returnSide !== Side.INVALID) { - if (returnSide !== classHierarchySide.first) { + if (returnAnnotation != null && returnSide !== Side.NONE && returnSide !== Side.INVALID) { + if (returnSide !== classSide) { registerMethodError( method, Error.RETURN_TYPE_IN_WRONG_CLASS, - classHierarchySide.first.annotation, - returnSide.annotation, - method + classAnnotation.renderSide(classSide), + returnAnnotation.renderSide(returnSide), + method.getAnnotation(methodAnnotation.annotationName) ) } } - return + break } } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/NestedClassSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/NestedClassSideOnlyInspection.kt index ae2ada78b..025b63fc3 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/NestedClassSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/NestedClassSideOnlyInspection.kt @@ -10,6 +10,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClass import com.siyeh.ig.BaseInspection import com.siyeh.ig.BaseInspectionVisitor @@ -32,10 +33,10 @@ class NestedClassSideOnlyInspection : BaseInspection() { } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { - val psiClass = infos[0] as PsiClass + val annotation = infos[0] as PsiAnnotation - return if (psiClass.isWritable) { - RemoveAnnotationInspectionGadgetsFix(psiClass, "Remove @SideOnly annotation from nested class") + return if (annotation.isWritable) { + RemoveAnnotationInspectionGadgetsFix(annotation, "Remove @SideOnly annotation from nested class") } else { null } @@ -54,26 +55,24 @@ class NestedClassSideOnlyInspection : BaseInspection() { return } - val classHierarchyList = SideOnlyUtil.checkClassHierarchy(aClass) - // The class lists are ordered from lowest to highest in the hierarchy - that is the first element in the list // is the most nested class, and the last element in the list is the top level class // // In this case, the higher-level classes take precedence, so if a class is annotated as @SideOnly.CLIENT and a nested class is // annotated as @SideOnly.SERVER, the nested class is the class that is in error, not the top level class var currentSide = Side.NONE - for (pair in classHierarchyList) { + for ((classAnnotation, classSide) in SideOnlyUtil.checkClassHierarchy(aClass)) { if (currentSide === Side.NONE) { // If currentSide is NONE, then a class hasn't declared yet what it is - if (pair.first !== Side.NONE && pair.first !== Side.INVALID) { - currentSide = pair.first + if (classSide !== Side.NONE && classSide !== Side.INVALID) { + currentSide = classSide } else { // We are only worried about this class return } - } else if (pair.first !== Side.NONE && pair.first !== Side.INVALID) { - if (pair.first !== currentSide) { - registerClassError(aClass, aClass) + } else if (classAnnotation != null && classSide !== Side.NONE && classSide !== Side.INVALID) { + if (classSide !== currentSide) { + registerClassError(aClass, aClass.getAnnotation(classAnnotation.annotationName)) } else { return } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/NewExpressionSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/NewExpressionSideOnlyInspection.kt index a8630b844..b94c56e59 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/NewExpressionSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/NewExpressionSideOnlyInspection.kt @@ -11,6 +11,7 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly import com.demonwav.mcdev.util.findContainingClass +import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClass import com.intellij.psi.PsiNewExpression import com.siyeh.ig.BaseInspection @@ -34,10 +35,10 @@ class NewExpressionSideOnlyInspection : BaseInspection() { } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { - val psiClass = infos[0] as PsiClass + val annotation = infos[0] as PsiAnnotation - return if (psiClass.isWritable) { - RemoveAnnotationInspectionGadgetsFix(psiClass, "Remove @SideOnly annotation from class declaration") + return if (annotation.isWritable) { + RemoveAnnotationInspectionGadgetsFix(annotation, "Remove @SideOnly annotation from class declaration") } else { null } @@ -54,41 +55,41 @@ class NewExpressionSideOnlyInspection : BaseInspection() { val psiElement = (element.resolve() ?: return) as? PsiClass ?: return - val list = SideOnlyUtil.checkClassHierarchy(psiElement) - + var classAnnotation: SideAnnotation? = null var classSide = Side.NONE - var offender: PsiClass? = null - for (pair in list) { - if (pair.first !== Side.NONE && pair.first !== Side.INVALID) { - classSide = pair.first - offender = pair.second + for ((hierarchyAnnotation, hierarchySide, clazz) in SideOnlyUtil.checkClassHierarchy(psiElement)) { + if (hierarchySide !== Side.NONE && hierarchySide !== Side.INVALID) { + classAnnotation = hierarchyAnnotation + classSide = hierarchySide + offender = clazz break } } - if (classSide === Side.NONE) { + if (classAnnotation == null || classSide == Side.NONE || offender == null) { return } // Check the class(es) the element is in val containingClass = expression.findContainingClass() ?: return - val containingClassSide = SideOnlyUtil.getSideForClass(containingClass) + val (_, containingClassSide) = SideOnlyUtil.getSideForClass(containingClass) // Check the method the element is in - val methodSide = SideOnlyUtil.checkElementInMethod(expression) + val (_, methodSide) = SideOnlyUtil.checkElementInMethod(expression) var classAnnotated = false - if (containingClassSide !== Side.NONE && containingClassSide !== Side.INVALID) { + if (containingClassSide !== Side.NONE && containingClassSide !== Side.INVALID + ) { if (containingClassSide !== classSide) { - registerError(expression, offender) + registerError(expression, offender.getAnnotation(classAnnotation.annotationName)) } classAnnotated = true } else { if (methodSide === Side.INVALID) { // It's not in a method - registerError(expression, offender) + registerError(expression, offender.getAnnotation(classAnnotation.annotationName)) return } } @@ -98,10 +99,10 @@ class NewExpressionSideOnlyInspection : BaseInspection() { if (methodSide === Side.NONE) { // If the class is properly annotated the method doesn't need to also be annotated if (!classAnnotated) { - registerError(expression, offender) + registerError(expression, offender.getAnnotation(classAnnotation.annotationName)) } } else { - registerError(expression, offender) + registerError(expression, offender.getAnnotation(classAnnotation.annotationName)) } } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/RemoveAnnotationInspectionGadgetsFix.kt b/src/main/kotlin/platform/forge/inspections/sideonly/RemoveAnnotationInspectionGadgetsFix.kt index 3cc1b404e..d90c193b8 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/RemoveAnnotationInspectionGadgetsFix.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/RemoveAnnotationInspectionGadgetsFix.kt @@ -10,32 +10,24 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly -import com.demonwav.mcdev.platform.forge.util.ForgeConstants import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project -import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.PsiAnnotation import com.intellij.structuralsearch.plugin.util.SmartPsiPointer import com.siyeh.ig.InspectionGadgetsFix import org.jetbrains.annotations.Nls -class RemoveAnnotationInspectionGadgetsFix(element: PsiModifierListOwner, private val name: String) : - InspectionGadgetsFix() { +class RemoveAnnotationInspectionGadgetsFix(element: PsiAnnotation, private val name: String) : InspectionGadgetsFix() { private val pointer: SmartPsiPointer = SmartPsiPointer(element) override fun doFix(project: Project, descriptor: ProblemDescriptor) { - val owner = pointer.element as? PsiModifierListOwner ?: return - val list = owner.modifierList ?: return - val annotation = list.findAnnotation(ForgeConstants.SIDE_ONLY_ANNOTATION) ?: return - - annotation.delete() + (pointer.element as? PsiAnnotation)?.delete() } @Nls override fun getName() = name @Nls - override fun getFamilyName(): String { - return name - } + override fun getFamilyName() = name } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/Side.kt b/src/main/kotlin/platform/forge/inspections/sideonly/Side.kt index 680fd82e8..5617e5bd3 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/Side.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/Side.kt @@ -12,12 +12,12 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly import com.intellij.openapi.util.Key -enum class Side(val annotation: String) { +enum class Side { - CLIENT("SideOnly.CLIENT"), - SERVER("SideOnly.SERVER"), - NONE("NONE"), - INVALID("INVALID"); + CLIENT, + SERVER, + NONE, + INVALID; companion object { val KEY = Key("MC_DEV_SIDE") diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt b/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt new file mode 100644 index 000000000..60ba4af5c --- /dev/null +++ b/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt @@ -0,0 +1,57 @@ +/* + * Minecraft Dev for IntelliJ + * + * https://minecraftdev.org + * + * Copyright (c) 2021 minecraft-dev + * + * MIT License + */ + +package com.demonwav.mcdev.platform.forge.inspections.sideonly + +import com.demonwav.mcdev.platform.forge.util.ForgeConstants + +data class SideAnnotation( + val annotationName: String, + val enumName: String, + val clientValue: String, + val serverValue: String, +) { + val simpleEnumName: String = enumName.substringAfterLast('.') + + fun renderSide(side: Side): String? = when (side) { + Side.CLIENT -> "$simpleEnumName.$clientValue" + Side.SERVER -> "$simpleEnumName.$serverValue" + else -> null + } + + fun renderFQSide(side: Side): String? = when (side) { + Side.CLIENT -> "$enumName.$clientValue" + Side.SERVER -> "$enumName.$serverValue" + else -> null + } + + companion object { + val KNOWN_ANNOTATIONS = listOf( + SideAnnotation( + ForgeConstants.SIDE_ONLY_ANNOTATION, + ForgeConstants.SIDE_ANNOTATION, + "CLIENT", + "SERVER" + ), + SideAnnotation( + "cpw.mods.fml.relauncher.SideOnly", + "cpw.mods.fml.relauncher.Side", + "CLIENT", + "SERVER" + ), + SideAnnotation( + "net.minecraftforge.api.distmarker.OnlyIn", + "net.minecraftforge.api.distmarker.Dist", + "CLIENT", + "DEDICATED_SERVER" + ), + ) + } +} diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/SideOnlyUtil.kt b/src/main/kotlin/platform/forge/inspections/sideonly/SideOnlyUtil.kt index 8612a8f56..2eb99e619 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/SideOnlyUtil.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/SideOnlyUtil.kt @@ -12,14 +12,15 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly import com.demonwav.mcdev.facet.MinecraftFacet import com.demonwav.mcdev.platform.forge.ForgeModuleType -import com.demonwav.mcdev.platform.forge.util.ForgeConstants +import com.demonwav.mcdev.util.findAnnotation import com.intellij.openapi.module.ModuleUtilCore -import com.intellij.openapi.util.Pair import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import com.intellij.psi.PsiEnumConstant import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod -import java.util.Arrays +import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.PsiReferenceExpression import java.util.LinkedList object SideOnlyUtil { @@ -35,32 +36,11 @@ object SideOnlyUtil { return facet != null && facet.isOfType(ForgeModuleType) } - private fun normalize(text: String): String { - if (text.startsWith(ForgeConstants.SIDE_ANNOTATION)) { - // We chop off the "net.minecraftforge.fml.relauncher." part here - return text.substring(text.lastIndexOf(".") - 4) - } - return text - } - - fun checkMethod(method: PsiMethod): Side { - val methodAnnotation = - // It's not annotated, which would be invalid if the element was annotated - method.modifierList.findAnnotation(ForgeConstants.SIDE_ONLY_ANNOTATION) - // (which, if we've gotten this far, is true) - ?: return Side.NONE - - // Check the value of the annotation - val methodValue = - // The annotation has no value yet, IntelliJ will give it's own error because a value is required - methodAnnotation.findAttributeValue("value") - ?: return Side.INVALID - - // Return the value of the annotation - return getFromName(methodValue.text) + fun checkMethod(method: PsiMethod): Pair { + return findSide(method) ?: (null to Side.NONE) } - fun checkElementInMethod(element: PsiElement): Side { + fun checkElementInMethod(element: PsiElement): Pair { var changingElement = element // Maybe there is a better way of doing this, I don't know, but crawl up the PsiElement stack in search of the // method this element is in. If it's not in a method it won't find one and the PsiMethod will be null @@ -81,13 +61,13 @@ object SideOnlyUtil { // No method was found if (method == null) { - return Side.INVALID + return (null to Side.INVALID) } return checkMethod(method) } - fun checkClassHierarchy(psiClass: PsiClass): List> { + fun checkClassHierarchy(psiClass: PsiClass): List> { val classList = LinkedList() classList.add(psiClass) @@ -104,70 +84,83 @@ object SideOnlyUtil { return classList.map { checkClass(it) } } - fun getSideForClass(psiClass: PsiClass): Side { + fun getSideForClass(psiClass: PsiClass): Pair { return getFirstSide(checkClassHierarchy(psiClass)) } - private fun checkClass(psiClass: PsiClass): Pair { - val side = psiClass.getUserData(Side.KEY) - if (side != null) { - return Pair(side, psiClass) + private fun checkClass(psiClass: PsiClass): Triple { + val knownSide = psiClass.getUserData(Side.KEY) + if (knownSide != null) { + return Triple(null, knownSide, psiClass) } - val modifierList = psiClass.modifierList ?: return Pair(Side.NONE, psiClass) - - // Check for the annotation, if it's not there then we return none, but this is - // usually irrelevant for classes - val annotation = modifierList.findAnnotation(ForgeConstants.SIDE_ONLY_ANNOTATION) - if (annotation == null) { - if (psiClass.supers.isEmpty()) { - return Pair(Side.NONE, psiClass) + // Check for a sided annotation, if it's not there then we search super classes + findSide(psiClass)?.let { (annotation, side) -> + if (side != Side.INVALID && side != Side.NONE) { + return Triple(annotation, side, psiClass) } - - // check the classes this class extends - return psiClass.supers.asSequence() - .filter { - // Prevent stack-overflow on cyclic dependencies - psiClass != it - } - .map { checkClassHierarchy(it) } - .firstOrNull { it.isNotEmpty() }?.let { Pair(it[0].getFirst(), psiClass) } ?: Pair(Side.NONE, psiClass) } - // Check the value on the annotation. If it's not there, IntelliJ will throw - // it's own error - val value = annotation.findAttributeValue("value") ?: return Pair(Side.INVALID, psiClass) + if (psiClass.supers.isEmpty()) { + return Triple(null, Side.NONE, psiClass) + } - return Pair(getFromName(value.text), psiClass) + // check the classes this class extends + return psiClass.supers.asSequence() + // Prevent stack-overflow on cyclic dependencies + .filter { psiClass != it } + .map { checkClassHierarchy(it) } + .firstOrNull { it.isNotEmpty() } + ?.let { + val (annotation, side, _) = it[0] + Triple(annotation, side, psiClass) + } ?: Triple(null, Side.NONE, psiClass) } - fun checkField(field: PsiField): Side { - // We check if this field has the @SideOnly annotation we are looking for + fun checkField(field: PsiField): Pair { + // We check if this field has a sided annotation we are looking for // If it doesn't, we aren't worried about it - val modifierList = field.modifierList ?: return Side.NONE - val annotation = modifierList.findAnnotation(ForgeConstants.SIDE_ONLY_ANNOTATION) ?: return Side.NONE - - // The value may not necessarily be set, but that will give an error by default as "value" is a - // required value for @SideOnly - val value = annotation.findAttributeValue("value") ?: return Side.INVALID - - // Finally, get the value of the SideOnly - return SideOnlyUtil.getFromName(value.text) + return findSide(field) ?: (null to Side.NONE) } - private fun getFromName(name: String): Side { - return when (normalize(name)) { - "Side.SERVER" -> Side.SERVER - "Side.CLIENT" -> Side.CLIENT - else -> Side.INVALID + /** + * @return a pair of the first sided annotation found on the given element with its side value. + * `null` is returned if no known side annotation was found. Side might be [Side.INVALID] but never [Side.NONE]. + */ + fun findSide(element: PsiModifierListOwner): Pair? { + for (sideAnnotation in SideAnnotation.KNOWN_ANNOTATIONS) { + val annotation = element.findAnnotation(sideAnnotation.annotationName) + ?: continue + + val sideValue = annotation.findAttributeValue("value") + ?: return sideAnnotation to Side.INVALID + + if (sideValue is PsiReferenceExpression) { + val referenced = sideValue.resolve() + if (referenced is PsiEnumConstant) { + val enumClass = referenced.containingClass + if (enumClass?.qualifiedName != sideAnnotation.enumName) { + continue + } + val side = when (referenced.name) { + sideAnnotation.clientValue -> Side.CLIENT + sideAnnotation.serverValue -> Side.SERVER + else -> continue + } + return sideAnnotation to side + } + } } + return null } - fun getFirstSide(list: List>): Side { - return list.firstOrNull { it.first !== Side.NONE }?.first ?: Side.NONE + fun getFirstSide(list: List>): Pair { + return list.firstOrNull { it.first != null && it.second != Side.NONE } + ?.let { it.first to it.second } + ?: (null to Side.NONE) } fun getSubArray(infos: Array): Array { - return Arrays.copyOfRange(infos, 1, infos.size - 1) + return infos.copyOfRange(1, infos.size - 1) } } diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/VariableUseSideOnlyInspection.kt b/src/main/kotlin/platform/forge/inspections/sideonly/VariableUseSideOnlyInspection.kt index 9cbd12b79..c56ff9b02 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/VariableUseSideOnlyInspection.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/VariableUseSideOnlyInspection.kt @@ -40,50 +40,50 @@ class VariableUseSideOnlyInspection : BaseInspection() { val declaration = expression.resolve() as? PsiFieldImpl ?: return - var elementSide = SideOnlyUtil.checkField(declaration) + var (elementAnnotation, elementSide) = SideOnlyUtil.checkField(declaration) // Check the class(es) the element is declared in val declarationContainingClass = declaration.containingClass ?: return - val declarationClassHierarchySides = SideOnlyUtil.checkClassHierarchy(declarationContainingClass) - - val declarationClassSide = SideOnlyUtil.getFirstSide(declarationClassHierarchySides) + val (declarationClassAnnotation, declarationClassSide) = + SideOnlyUtil.getFirstSide(SideOnlyUtil.checkClassHierarchy(declarationContainingClass)) // The element inherits the @SideOnly from it's parent class if it doesn't explicitly set it itself var inherited = false if (declarationClassSide !== Side.NONE && (elementSide === Side.INVALID || elementSide === Side.NONE)) { inherited = true + elementAnnotation = declarationClassAnnotation elementSide = declarationClassSide } - if (elementSide === Side.INVALID || elementSide === Side.NONE) { + if (elementAnnotation == null || elementSide === Side.INVALID || elementSide === Side.NONE) { return } // Check the class(es) the element is in val containingClass = expression.findContainingClass() ?: return - val classSide = SideOnlyUtil.getSideForClass(containingClass) + val (classAnnotation, classSide) = SideOnlyUtil.getSideForClass(containingClass) var classAnnotated = false - if (classSide !== Side.NONE && classSide !== Side.INVALID) { + if (classAnnotation != null && classSide !== Side.NONE && classSide !== Side.INVALID) { if (classSide !== elementSide) { if (inherited) { registerError( expression.element, Error.ANNOTATED_CLASS_VAR_IN_CROSS_ANNOTATED_CLASS_METHOD, - elementSide.annotation, - classSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + classAnnotation.renderSide(classSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( expression.element, Error.ANNOTATED_VAR_IN_CROSS_ANNOTATED_CLASS_METHOD, - elementSide.annotation, - classSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + classAnnotation.renderSide(classSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } } @@ -91,10 +91,10 @@ class VariableUseSideOnlyInspection : BaseInspection() { } // Check the method the element is in - val methodSide = SideOnlyUtil.checkElementInMethod(expression) + val (methodAnnotation, methodSide) = SideOnlyUtil.checkElementInMethod(expression) // Put error on for method - if (elementSide !== methodSide && methodSide !== Side.INVALID) { + if (methodAnnotation != null && elementSide !== methodSide && methodSide !== Side.INVALID) { if (methodSide === Side.NONE) { // If the class is properly annotated the method doesn't need to also be annotated if (!classAnnotated) { @@ -102,17 +102,17 @@ class VariableUseSideOnlyInspection : BaseInspection() { registerError( expression.element, Error.ANNOTATED_CLASS_VAR_IN_UNANNOTATED_METHOD, - elementSide.annotation, + elementAnnotation.renderSide(elementSide), null, - declaration + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( expression.element, Error.ANNOTATED_VAR_IN_UNANNOTATED_METHOD, - elementSide.annotation, + elementAnnotation.renderSide(elementSide), null, - declaration + declaration.getAnnotation(elementAnnotation.annotationName) ) } } @@ -121,17 +121,17 @@ class VariableUseSideOnlyInspection : BaseInspection() { registerError( expression.element, Error.ANNOTATED_CLASS_VAR_IN_CROSS_ANNOTATED_METHOD, - elementSide.annotation, - methodSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + methodAnnotation.renderSide(methodSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } else { registerError( expression.element, Error.ANNOTATED_VAR_IN_CROSS_ANNOTATED_METHOD, - elementSide.annotation, - methodSide.annotation, - declaration + elementAnnotation.renderSide(elementSide), + methodAnnotation.renderSide(methodSide), + declaration.getAnnotation(elementAnnotation.annotationName) ) } } diff --git a/src/main/kotlin/platform/mixin/inspection/UnusedMixinInspection.kt b/src/main/kotlin/platform/mixin/inspection/UnusedMixinInspection.kt index c49c78ee1..cb403590f 100644 --- a/src/main/kotlin/platform/mixin/inspection/UnusedMixinInspection.kt +++ b/src/main/kotlin/platform/mixin/inspection/UnusedMixinInspection.kt @@ -15,8 +15,10 @@ import com.demonwav.mcdev.platform.forge.inspections.sideonly.SideOnlyUtil import com.demonwav.mcdev.platform.mixin.MixinModule import com.demonwav.mcdev.platform.mixin.config.MixinConfig import com.demonwav.mcdev.platform.mixin.util.isMixin +import com.demonwav.mcdev.platform.mixin.util.mixinTargets import com.demonwav.mcdev.util.findModule import com.demonwav.mcdev.util.fullQualifiedName +import com.demonwav.mcdev.util.mapFirstNotNull import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemsHolder @@ -61,7 +63,12 @@ class UnusedMixinInspection : MixinInspection() { val bestQuickFixFile = bestQuickFixConfig?.file val qualifiedName = clazz.fullQualifiedName if (bestQuickFixFile != null && qualifiedName != null) { - val quickFix = QuickFix(bestQuickFixFile, qualifiedName, SideOnlyUtil.getSideForClass(clazz)) + var side = SideOnlyUtil.getSideForClass(clazz).second + if (side == Side.NONE || side == Side.INVALID) { + side = clazz.mixinTargets.mapFirstNotNull(SideOnlyUtil::getSideForClass)?.second + ?: Side.NONE + } + val quickFix = QuickFix(bestQuickFixFile, qualifiedName, side) holder.registerProblem(problematicElement, "Mixin not found in any mixin config", quickFix) } else { holder.registerProblem(problematicElement, "Mixin not found in any mixin config") @@ -76,7 +83,14 @@ class UnusedMixinInspection : MixinInspection() { private val qualifiedName: String, private val side: Side ) : LocalQuickFix { - override fun getName() = "Add to Mixin config" + + private val sideDisplayName = when (side) { + Side.CLIENT -> "client" + Side.SERVER -> "server" + else -> "common" + } + + override fun getName() = "Add to Mixin config ($sideDisplayName side)" override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val psiFile = PsiManager.getInstance(project).findFile(quickFixFile) as? JsonFile ?: return From bd54f00daabb89281aa0344ebb95ce0b84722fe7 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 13 Aug 2021 22:03:14 +0200 Subject: [PATCH 6/7] Small ForgeConstants cleanup --- .../platform/forge/inspections/sideonly/SideAnnotation.kt | 6 ++---- src/main/kotlin/platform/forge/util/ForgeConstants.kt | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt b/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt index 60ba4af5c..e321f509f 100644 --- a/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt +++ b/src/main/kotlin/platform/forge/inspections/sideonly/SideAnnotation.kt @@ -10,8 +10,6 @@ package com.demonwav.mcdev.platform.forge.inspections.sideonly -import com.demonwav.mcdev.platform.forge.util.ForgeConstants - data class SideAnnotation( val annotationName: String, val enumName: String, @@ -35,8 +33,8 @@ data class SideAnnotation( companion object { val KNOWN_ANNOTATIONS = listOf( SideAnnotation( - ForgeConstants.SIDE_ONLY_ANNOTATION, - ForgeConstants.SIDE_ANNOTATION, + "net.minecraftforge.fml.relauncher.SideOnly", + "net.minecraftforge.fml.relauncher.Side", "CLIENT", "SERVER" ), diff --git a/src/main/kotlin/platform/forge/util/ForgeConstants.kt b/src/main/kotlin/platform/forge/util/ForgeConstants.kt index 030bad713..f90fd37cd 100644 --- a/src/main/kotlin/platform/forge/util/ForgeConstants.kt +++ b/src/main/kotlin/platform/forge/util/ForgeConstants.kt @@ -12,8 +12,6 @@ package com.demonwav.mcdev.platform.forge.util object ForgeConstants { - const val SIDE_ONLY_ANNOTATION = "net.minecraftforge.fml.relauncher.SideOnly" - const val SIDE_ANNOTATION = "net.minecraftforge.fml.relauncher.Side" const val SIDED_PROXY_ANNOTATION = "net.minecraftforge.fml.common.SidedProxy" const val MOD_ANNOTATION = "net.minecraftforge.fml.common.Mod" const val CORE_MOD_INTERFACE = "net.minecraftforge.fml.relauncher.IFMLLoadingPlugin" @@ -26,7 +24,6 @@ object ForgeConstants { const val NETWORK_MESSAGE = "net.minecraftforge.fml.common.network.simpleimpl.IMessage" const val NETWORK_MESSAGE_HANDLER = "net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler" const val MCMOD_INFO = "mcmod.info" - const val META_INF = "META-INF" const val MODS_TOML = "mods.toml" const val PACK_MCMETA = "pack.mcmeta" From fdc8f3a33953d0de2c8aba5f9b12457b49c7a7f6 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 13 Aug 2021 22:05:26 +0200 Subject: [PATCH 7/7] Version 1.5.13 --- gradle.properties | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 860a95561..8e43241e5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ kotlin.code.style=official ideaVersion = 2020.1 ideaVersionName = 2020.1 -coreVersion = 1.5.12 +coreVersion = 1.5.13 downloadIdeaSources = true pluginTomlVersion = 0.2.131.3366-201 diff --git a/readme.md b/readme.md index 3f7882216..0f647e260 100644 --- a/readme.md +++ b/readme.md @@ -40,7 +40,7 @@ Minecraft Development for IntelliJ -Info and Documentation [![Current Release](https://img.shields.io/badge/release-1.5.12-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327) +Info and Documentation [![Current Release](https://img.shields.io/badge/release-1.5.13-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327) ----------------------