diff --git a/build.gradle b/build.gradle index 5ea36f38c9..f482abad18 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,27 @@ -//version: 1644612407 +//version: 1660899027 /* -DO NOT CHANGE THIS FILE! + DO NOT CHANGE THIS FILE! + Also, you may replace this file at any time if there is an update available. + Please check https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/build.gradle for updates. + */ -Also, you may replace this file at any time if there is an update available. -Please check https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/build.gradle for updates. -*/ - -import org.gradle.internal.logging.text.StyledTextOutput -import org.gradle.internal.logging.text.StyledTextOutputFactory -import org.gradle.internal.logging.text.StyledTextOutput.Style import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.internal.logging.text.StyledTextOutput.Style +import org.gradle.internal.logging.text.StyledTextOutputFactory +import java.nio.file.Files +import java.nio.file.Paths import java.util.concurrent.TimeUnit +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream buildscript { repositories { + mavenCentral() + maven { name 'forge' url 'https://maven.minecraftforge.net' @@ -38,22 +43,30 @@ buildscript { classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.7' } } - plugins { id 'java-library' id 'idea' id 'eclipse' id 'scala' id 'maven-publish' - id 'org.jetbrains.kotlin.jvm' version '1.5.30' apply false - id 'org.jetbrains.kotlin.kapt' version '1.5.30' apply false + id 'org.jetbrains.kotlin.jvm' version '1.5.30' apply false + id 'org.jetbrains.kotlin.kapt' version '1.5.30' apply false + id 'com.google.devtools.ksp' version '1.5.30-1.0.0' apply false id 'org.ajoberstar.grgit' version '4.1.1' id 'com.github.johnrengelman.shadow' version '4.0.4' - id 'com.palantir.git-version' version '0.13.0' apply false + id 'com.palantir.git-version' version '0.13.0' apply false id 'de.undercouch.download' version '5.0.1' - id 'com.github.gmazzo.buildconfig' version '3.0.3' apply false + id 'com.github.gmazzo.buildconfig' version '3.0.3' apply false + id 'com.diffplug.spotless' version '6.7.2' apply false +} +verifySettingsGradle() + +dependencies { + implementation 'com.diffplug:blowdryer:1.6.0' } +apply plugin: 'com.diffplug.blowdryer' + if (project.file('.git/HEAD').isFile()) { apply plugin: 'com.palantir.git-version' } @@ -78,6 +91,13 @@ idea { } } +boolean disableSpotless = project.hasProperty("disableSpotless") ? project.disableSpotless.toBoolean() : false + +if (!disableSpotless) { + apply plugin: 'com.diffplug.spotless' + apply from: Blowdryer.file('spotless.gradle') +} + if(JavaVersion.current() != JavaVersion.VERSION_1_8) { throw new GradleException("This project requires Java 8, but it's running on " + JavaVersion.current()) } @@ -103,8 +123,11 @@ checkPropertyExists("containsMixinsAndOrCoreModOnly") checkPropertyExists("usesShadowedDependencies") checkPropertyExists("developmentEnvironmentUserName") -boolean noPublishedSources = project.findProperty("noPublishedSources") ? project.noPublishedSources.toBoolean() : false - +boolean noPublishedSources = project.hasProperty("noPublishedSources") ? project.noPublishedSources.toBoolean() : false +boolean usesMixinDebug = project.hasProperty('usesMixinDebug') ?: project.usesMixins.toBoolean() +boolean forceEnableMixins = project.hasProperty('forceEnableMixins') ? project.forceEnableMixins.toBoolean() : false +String channel = project.hasProperty('channel') ? project.channel : 'stable' +String mappingsVersion = project.hasProperty('mappingsVersion') ? project.mappingsVersion : '12' String javaSourceDir = "src/main/java/" String scalaSourceDir = "src/main/scala/" String kotlinSourceDir = "src/main/kotlin/" @@ -175,7 +198,7 @@ configurations.all { try { 'git config core.fileMode false'.execute() } -catch (Exception e) { +catch (Exception ignored) { out.style(Style.Failure).println("git isn't installed at all") } @@ -185,11 +208,11 @@ String versionOverride = System.getenv("VERSION") ?: null try { identifiedVersion = versionOverride == null ? gitVersion() : versionOverride } -catch (Exception e) { +catch (Exception ignored) { out.style(Style.Failure).text( - 'This mod must be version controlled by Git AND the repository must provide at least one tag,\n' + - 'or the VERSION override must be set! ').style(Style.SuccessHeader).text('(Do NOT download from GitHub using the ZIP option, instead\n' + - 'clone the repository, see ').style(Style.Info).text('https://gtnh.miraheze.org/wiki/Development').style(Style.SuccessHeader).println(' for details.)' + 'This mod must be version controlled by Git AND the repository must provide at least one tag,\n' + + 'or the VERSION override must be set! ').style(Style.SuccessHeader).text('(Do NOT download from GitHub using the ZIP option, instead\n' + + 'clone the repository, see ').style(Style.Info).text('https://gtnh.miraheze.org/wiki/Development').style(Style.SuccessHeader).println(' for details.)' ) versionOverride = 'NO-GIT-TAG-SET' identifiedVersion = versionOverride @@ -199,7 +222,7 @@ ext { modVersion = identifiedVersion } -if( identifiedVersion.equals(versionOverride) ) { +if(identifiedVersion == versionOverride) { out.style(Style.Failure).text('Override version to ').style(Style.Identifier).text(modVersion).style(Style.Failure).println('!\7') } @@ -214,13 +237,17 @@ else { def arguments = [] def jvmArguments = [] -if(usesMixins.toBoolean()) { +if (usesMixins.toBoolean() || forceEnableMixins) { arguments += [ "--tweakClass org.spongepowered.asm.launch.MixinTweaker" ] - jvmArguments += [ - "-Dmixin.debug.countInjections=true", "-Dmixin.debug.verbose=true", "-Dmixin.debug.export=true" - ] + if (usesMixinDebug.toBoolean()) { + jvmArguments += [ + "-Dmixin.debug.countInjections=true", + "-Dmixin.debug.verbose=true", + "-Dmixin.debug.export=true" + ] + } } minecraft { @@ -275,7 +302,7 @@ repositories { name 'Overmind forge repo mirror' url 'https://gregtech.overminddl1.com/' } - if(usesMixins.toBoolean()) { + if(usesMixins.toBoolean() || forceEnableMixins) { maven { name 'sponge' url 'https://repo.spongepowered.org/repository/maven-public' @@ -292,6 +319,8 @@ dependencies { annotationProcessor('com.google.guava:guava:24.1.1-jre') annotationProcessor('com.google.code.gson:gson:2.8.6') annotationProcessor('org.spongepowered:mixin:0.8-SNAPSHOT') + } + if(usesMixins.toBoolean() || forceEnableMixins) { // using 0.8 to workaround a issue in 0.7 which fails mixin application compile('com.github.GTNewHorizons:SpongePoweredMixin:0.7.12-GTNH') { // Mixin includes a lot of dependencies that are too up-to-date @@ -312,18 +341,23 @@ def refMap = "${tasks.compileJava.temporaryDir}" + File.separator + mixingConfig def mixinSrg = "${tasks.reobf.temporaryDir}" + File.separator + "mixins.srg" task generateAssets { - if(usesMixins.toBoolean()) { - getFile("/src/main/resources/mixins." + modId + ".json").text = """{ + if (usesMixins.toBoolean()) { + def mixinConfigFile = getFile("/src/main/resources/mixins." + modId + ".json"); + if (!mixinConfigFile.exists()) { + mixinConfigFile.text = """{ "required": true, "minVersion": "0.7.11", "package": "${modGroup}.${mixinsPackage}", "plugin": "${modGroup}.${mixinPlugin}", "refmap": "${mixingConfigRefMap}", "target": "@env(DEFAULT)", - "compatibilityLevel": "JAVA_8" + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [], + "server": [] } - """ + } } } @@ -344,7 +378,10 @@ shadowJar { } minimize() // This will only allow shading for actually used classes - configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] + configurations = [ + project.configurations.shadowImplementation, + project.configurations.shadowCompile + ] dependsOn(relocateShadowJar) } @@ -366,7 +403,7 @@ jar { } reobf { - if(usesMixins.toBoolean()) { + if(usesMixins.toBoolean() && file(mixinSrg).exists()) { addExtraSrgFile mixinSrg } } @@ -415,6 +452,7 @@ processResources { // this will ensure that this task is redone when the versions change. inputs.property "version", project.version inputs.property "mcversion", project.minecraft.version + exclude("spotless.gradle") // replace stuff in mcmod.info, nothing else from(sourceSets.main.resources.srcDirs) { @@ -422,9 +460,9 @@ processResources { // replace modVersion and minecraftVersion expand "minecraftVersion": project.minecraft.version, - "modVersion": modVersion, - "modId": modId, - "modName": modName + "modVersion": modVersion, + "modId": modId, + "modName": modName } if(usesMixins.toBoolean()) { @@ -434,12 +472,13 @@ processResources { // copy everything else that's not the mcmod.info from(sourceSets.main.resources.srcDirs) { exclude 'mcmod.info' + exclude 'spotless.gradle' } } def getManifestAttributes() { def manifestAttributes = [:] - if(containsMixinsAndOrCoreModOnly.toBoolean() == false && (usesMixins.toBoolean() || coreModClass)) { + if(!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) { manifestAttributes += ["FMLCorePluginContainsFMLMod": true] } @@ -455,14 +494,14 @@ def getManifestAttributes() { manifestAttributes += [ "TweakClass" : "org.spongepowered.asm.launch.MixinTweaker", "MixinConfigs" : "mixins." + modId + ".json", - "ForceLoadAsMod" : containsMixinsAndOrCoreModOnly.toBoolean() == false + "ForceLoadAsMod" : !containsMixinsAndOrCoreModOnly.toBoolean() ] } return manifestAttributes } task sourcesJar(type: Jar) { - from (sourceSets.main.allJava) + from (sourceSets.main.allSource) from (file("$projectDir/LICENSE")) getArchiveClassifier().set('sources') } @@ -482,7 +521,10 @@ task shadowDevJar(type: ShadowJar) { } minimize() // This will only allow shading for actually used classes - configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] + configurations = [ + project.configurations.shadowImplementation, + project.configurations.shadowCompile + ] } task relocateShadowDevJar(type: ConfigureShadowRelocation) { @@ -517,7 +559,7 @@ task devJar(type: Jar) { } task apiJar(type: Jar) { - from (sourceSets.main.allJava) { + from (sourceSets.main.allSource) { include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + '/**' } @@ -548,6 +590,9 @@ tasks.withType(GenerateModuleMetadata) { enabled = false } +// workaround variable hiding in pom processing +def projectConfigs = project.configurations + publishing { publications { maven(MavenPublication) { @@ -556,7 +601,7 @@ publishing { artifact source: shadowJar, classifier: "" } if(!noPublishedSources) { - artifact source: sourcesJar, classifier: "src" + artifact source: sourcesJar, classifier: "sources" } artifact source: usesShadowedDependencies.toBoolean() ? shadowDevJar : devJar, classifier: "dev" if (apiPackage) { @@ -568,16 +613,23 @@ publishing { // Using the identified version, not project.version as it has the prepended 1.7.10 version = System.getenv("RELEASE_VERSION") ?: identifiedVersion - // remove extra garbage from who knows where + // remove extra garbage from minecraft and minecraftDeps configuration pom.withXml { - def badPomGroup = ['net.minecraft', 'com.google.code.findbugs', 'org.ow2.asm', 'com.typesafe.akka', 'com.typesafe', 'org.scala-lang', - 'org.scala-lang.plugins', 'net.sf.jopt-simple', 'lzma', 'com.mojang', 'org.apache.commons', 'org.apache.httpcomponents', - 'commons-logging', 'java3d', 'net.sf.trove4j', 'com.ibm.icu', 'com.paulscode', 'io.netty', 'com.google.guava', - 'commons-io', 'commons-codec', 'net.java.jinput', 'net.java.jutils', 'com.google.code.gson', 'org.apache.logging.log4j', - 'org.lwjgl.lwjgl', 'tv.twitch', 'org.jetbrains.kotlin', ''] + def badArtifacts = [:].withDefault {[] as Set} + for (configuration in [ + projectConfigs.minecraft, + projectConfigs.minecraftDeps + ]) { + for (dependency in configuration.allDependencies) { + badArtifacts[dependency.group == null ? "" : dependency.group] += dependency.name + } + } + // example for specifying extra stuff to ignore + // badArtifacts["org.example.group"] += "artifactName" + Node pomNode = asNode() pomNode.dependencies.'*'.findAll() { - badPomGroup.contains(it.groupId.text()) + badArtifacts[it.groupId.text()].contains(it.artifactId.text()) }.each() { it.parent().remove(it) } @@ -605,7 +657,7 @@ task updateBuildScript { } } -if (isNewBuildScriptVersionAvailable(projectDir.toString())) { +if (!project.getGradle().startParameter.isOffline() && isNewBuildScriptVersionAvailable(projectDir.toString())) { if (autoUpdateBuildScript.toBoolean()) { performBuildScriptUpdate(projectDir.toString()) } else { @@ -616,12 +668,26 @@ if (isNewBuildScriptVersionAvailable(projectDir.toString())) { static URL availableBuildScriptUrl() { new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/main/build.gradle") } +static URL exampleSettingsGradleUrl() { + new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/main/settings.gradle.example") +} + + +def verifySettingsGradle() { + def settingsFile = getFile("settings.gradle") + if (!settingsFile.exists()) { + println("Downloading default settings.gradle") + exampleSettingsGradleUrl().withInputStream { i -> settingsFile.withOutputStream { it << i } } + throw new GradleException("Settings.gradle has been updated, please re-run task.") + } +} boolean performBuildScriptUpdate(String projectDir) { if (isNewBuildScriptVersionAvailable(projectDir)) { def buildscriptFile = getFile("build.gradle") availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } } out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!") + verifySettingsGradle() return true } return false @@ -652,7 +718,103 @@ configure(updateBuildScript) { description = 'Updates the build script to the latest version' } -// Deobfuscation +// Parameter Deobfuscation + +task deobfParams { + doLast { + + String mcpDir = "$project.gradle.gradleUserHomeDir/caches/minecraft/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion" + String mcpZIP = "$mcpDir/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip" + String paramsCSV = "$mcpDir/params.csv" + + download.run { + src "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion-$minecraftVersion/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip" + dest mcpZIP + overwrite false + } + + if(!file(paramsCSV).exists()) { + println("Extracting MCP archive ...") + unzip(mcpZIP, mcpDir) + } + + println("Parsing params.csv ...") + Map params = new HashMap<>() + Files.lines(Paths.get(paramsCSV)).forEach{line -> + String[] cells = line.split(",") + if(cells.length > 2 && cells[0].matches("p_i?\\d+_\\d+_")) { + params.put(cells[0], cells[1]) + } + } + + out.style(Style.Success).println("Modified ${replaceParams(file("$projectDir/src/main/java"), params)} files!") + out.style(Style.Failure).println("Don't forget to verify that the code still works as before!\n It could be broken due to duplicate variables existing now\n or parameters taking priority over other variables.") + } +} + +static int replaceParams(File file, Map params) { + int fileCount = 0 + + if(file.isDirectory()) { + for(File f : file.listFiles()) { + fileCount += replaceParams(f, params) + } + return fileCount + } + println("Visiting ${file.getName()} ...") + try { + String content = new String(Files.readAllBytes(file.toPath())) + int hash = content.hashCode() + params.forEach{key, value -> + content = content.replaceAll(key, value) + } + if(hash != content.hashCode()) { + Files.write(file.toPath(), content.getBytes("UTF-8")) + return 1 + } + } catch(Exception e) { + e.printStackTrace() + } + return 0 +} + +// Credit: bitsnaps (https://gist.github.com/bitsnaps/00947f2dce66f4bbdabc67d7e7b33681) +static unzip(String zipFileName, String outputDir) { + byte[] buffer = new byte[16384] + ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFileName)) + ZipEntry zipEntry = zis.getNextEntry() + while (zipEntry != null) { + File newFile = new File(outputDir + File.separator, zipEntry.name) + if (zipEntry.isDirectory()) { + if (!newFile.isDirectory() && !newFile.mkdirs()) { + throw new IOException("Failed to create directory $newFile") + } + } else { + // fix for Windows-created archives + File parent = newFile.parentFile + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Failed to create directory $parent") + } + // write file content + FileOutputStream fos = new FileOutputStream(newFile) + int len = 0 + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len) + } + fos.close() + } + zipEntry = zis.getNextEntry() + } + zis.closeEntry() + zis.close() +} + +configure(deobfParams) { + group = 'forgegradle' + description = 'Rename all obfuscated parameter names inherited from Minecraft classes' +} + +// Dependency Deobfuscation def deobf(String sourceURL) { try { @@ -665,7 +827,7 @@ def deobf(String sourceURL) { fileName = fileName.substring(lastSlash + 1) } //get rid of extension: - if(fileName.endsWith(".jar")) { + if(fileName.endsWith(".jar") || fileName.endsWith(".litemod")) { fileName = fileName.substring(0, fileName.lastIndexOf(".")) } @@ -677,26 +839,41 @@ def deobf(String sourceURL) { Collections.reverse(parts) hostName = String.join(".", parts) - return deobf(sourceURL, hostName + "/" + fileName) + return deobf(sourceURL, "$hostName/$fileName") } catch(Exception e) { - return deobf(sourceURL, "deobf/" + String.valueOf(sourceURL.hashCode())) + return deobf(sourceURL, "deobf/${sourceURL.hashCode()}") } } // The method above is to be preferred. Use this method if the filename is not at the end of the URL. -def deobf(String sourceURL, String fileName) { - String cacheDir = System.getProperty("user.home") + "/.gradle/caches/" - String bon2Dir = cacheDir + "forge_gradle/deobf" - String bon2File = bon2Dir + "/BON2-2.5.0.jar" - String obfFile = cacheDir + "modules-2/files-2.1/" + fileName + ".jar" - String deobfFile = cacheDir + "modules-2/files-2.1/" + fileName + "-deobf.jar" +def deobf(String sourceURL, String rawFileName) { + String bon2Version = "2.5.1" + String fileName = URLDecoder.decode(rawFileName, "UTF-8") + String cacheDir = "$project.gradle.gradleUserHomeDir/caches" + String bon2Dir = "$cacheDir/forge_gradle/deobf" + String bon2File = "$bon2Dir/BON2-${bon2Version}.jar" + String obfFile = "$cacheDir/modules-2/files-2.1/${fileName}.jar" + String deobfFile = "$cacheDir/modules-2/files-2.1/${fileName}-deobf.jar" if(file(deobfFile).exists()) { return files(deobfFile) } + String mappingsVer + String remoteMappings = project.hasProperty('remoteMappings') ? project.remoteMappings : 'https://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/' + if(remoteMappings) { + String id = "${forgeVersion.split("\\.")[3]}-$minecraftVersion" + String mappingsZIP = "$cacheDir/forge_gradle/maven_downloader/de/oceanlabs/mcp/mcp_snapshot_nodoc/$id/mcp_snapshot_nodoc-${id}.zip" + + zipMappings(mappingsZIP, remoteMappings, bon2Dir) + + mappingsVer = "snapshot_$id" + } else { + mappingsVer = "${channel}_$mappingsVersion" + } + download.run { - src 'https://github.com/GTNewHorizons/BON2/releases/download/2.5.0/BON2-2.5.0.CUSTOM-all.jar' + src "http://jenkins.usrv.eu:8081/nexus/content/repositories/releases/com/github/parker8283/BON2/$bon2Version-CUSTOM/BON2-$bon2Version-CUSTOM-all.jar" dest bon2File quiet true overwrite false @@ -710,22 +887,56 @@ def deobf(String sourceURL, String fileName) { } exec { - commandLine 'java', '-jar', bon2File, '--inputJar', obfFile, '--outputJar', deobfFile, '--mcVer', '1.7.10', '--mappingsVer', 'stable_12', '--notch' + commandLine 'java', '-jar', bon2File, '--inputJar', obfFile, '--outputJar', deobfFile, '--mcVer', minecraftVersion, '--mappingsVer', mappingsVer, '--notch' workingDir bon2Dir - standardOutput = new ByteArrayOutputStream() + standardOutput = new FileOutputStream("${deobfFile}.log") } return files(deobfFile) } +def zipMappings(String zipPath, String url, String bon2Dir) { + File zipFile = new File(zipPath) + if(zipFile.exists()) { + return + } + + String fieldsCache = "$bon2Dir/data/fields.csv" + String methodsCache = "$bon2Dir/data/methods.csv" + + download.run { + src "${url}fields.csv" + dest fieldsCache + quiet true + } + download.run { + src "${url}methods.csv" + dest methodsCache + quiet true + } + + zipFile.getParentFile().mkdirs() + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile)) + + zos.putNextEntry(new ZipEntry("fields.csv")) + Files.copy(Paths.get(fieldsCache), zos) + zos.closeEntry() + + zos.putNextEntry(new ZipEntry("methods.csv")) + Files.copy(Paths.get(methodsCache), zos) + zos.closeEntry() + + zos.close() +} + // Helper methods def checkPropertyExists(String propertyName) { - if (project.hasProperty(propertyName) == false) { + if (!project.hasProperty(propertyName)) { throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/gradle.properties") } } def getFile(String relativePath) { return new File(projectDir, relativePath) -} +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 04695e351a..e38c371693 100644 --- a/gradle.properties +++ b/gradle.properties @@ -62,3 +62,11 @@ usesShadowedDependencies = false # Optional parameter to customize the produced artifacts. Use this to preserver artifact naming when migrating older # projects. New projects should not use this parameter. #customArchiveBaseName = + +# Optional parameter to prevent the source code from being published +# noPublishedSources = + +# Uncomment this to disable spotless checks +# This should only be uncommented to keep it easier to sync with upstream/other forks. +# That is, if there is no other active fork/upstream, NEVER change this. +disableSpotless = true diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..c4cf340211 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,11 @@ +plugins { + id 'com.diffplug.blowdryerSetup' version '1.6.0' +} + +apply plugin: 'com.diffplug.blowdryerSetup' + +blowdryerSetup { +// Pulls the spotless config from the example mod repository + github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.1.4') + //devLocal '.' // Use this when testing config updates locally +} diff --git a/src/main/java/vazkii/botania/common/core/handler/BiomeDecorationHandler.java b/src/main/java/vazkii/botania/common/core/handler/BiomeDecorationHandler.java index b5fa63798e..af6a76ac69 100644 --- a/src/main/java/vazkii/botania/common/core/handler/BiomeDecorationHandler.java +++ b/src/main/java/vazkii/botania/common/core/handler/BiomeDecorationHandler.java @@ -23,6 +23,8 @@ import cpw.mods.fml.common.eventhandler.EventPriority; import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import java.util.Arrays; + public class BiomeDecorationHandler { @SubscribeEvent(priority = EventPriority.LOWEST) @@ -37,47 +39,73 @@ else if(event.world.getBiomeGenForCoords(event.chunkX, event.chunkZ) instanceof if(!flowers) return; - int dist = Math.min(8, Math.max(1, ConfigHandler.flowerPatchSize)); - for(int i = 0; i < ConfigHandler.flowerQuantity; i++) { - if(event.rand.nextInt(ConfigHandler.flowerPatchChance) == 0) { - int x = event.chunkX + event.rand.nextInt(16) + 8; - int z = event.chunkZ + event.rand.nextInt(16) + 8; - int y = event.world.getTopSolidOrLiquidBlock(x, z); - - int color = event.rand.nextInt(16); - boolean primus = event.rand.nextInt(380) == 0; - - for(int j = 0; j < ConfigHandler.flowerDensity * ConfigHandler.flowerPatchChance; j++) { - int x1 = x + event.rand.nextInt(dist * 2) - dist; - int y1 = y + event.rand.nextInt(4) - event.rand.nextInt(4); - int z1 = z + event.rand.nextInt(dist * 2) - dist; + if (ConfigHandler.flowerDimensionWhitelist.length == 0 + || Arrays.stream(ConfigHandler.flowerDimensionWhitelist).anyMatch( + d -> d == event.world.provider.dimensionId + )) { + if (Arrays.stream(ConfigHandler.flowerDimensionBlacklist).noneMatch( + d -> d == event.world.provider.dimensionId + )) { + generateFlowers(event); + } + } - if(event.world.isAirBlock(x1, y1, z1) && (!event.world.provider.hasNoSky || y1 < 127) && ModBlocks.flower.canBlockStay(event.world, x1, y1, z1)) { - if(primus) { - event.world.setBlock(x1, y1, z1, ModBlocks.specialFlower, 0, 2); - TileSpecialFlower flower = (TileSpecialFlower) event.world.getTileEntity(x1, y1, z1); - flower.setSubTile(event.rand.nextBoolean() ? LibBlockNames.SUBTILE_NIGHTSHADE_PRIME : LibBlockNames.SUBTILE_DAYBLOOM_PRIME); - SubTileDaybloom subtile = (SubTileDaybloom) flower.getSubTile(); - subtile.setPrimusPosition(); - } else { - event.world.setBlock(x1, y1, z1, ModBlocks.flower, color, 2); - if(event.rand.nextDouble() < ConfigHandler.flowerTallChance && ((BlockModFlower) ModBlocks.flower).func_149851_a(event.world, x1, y1, z1, false)) - BlockModFlower.placeDoubleFlower(event.world, x1, y1, z1, color, 0); - } - } - } + if (ConfigHandler.mushroomDimensionWhitelist.length == 0 + || Arrays.stream(ConfigHandler.mushroomDimensionWhitelist).anyMatch( + d -> d == event.world.provider.dimensionId + )) { + if (Arrays.stream(ConfigHandler.mushroomDimensionBlacklist).noneMatch( + d -> d == event.world.provider.dimensionId + )) { + generateMushrooms(event); } } + } + } - for(int i = 0; i < ConfigHandler.mushroomQuantity; i++) { + private void generateFlowers(DecorateBiomeEvent.Decorate event) { + int dist = Math.min(8, Math.max(1, ConfigHandler.flowerPatchSize)); + for(int i = 0; i < ConfigHandler.flowerQuantity; i++) { + if(event.rand.nextInt(ConfigHandler.flowerPatchChance) == 0) { int x = event.chunkX + event.rand.nextInt(16) + 8; int z = event.chunkZ + event.rand.nextInt(16) + 8; - int y = event.rand.nextInt(26) + 4; + int y = event.world.getTopSolidOrLiquidBlock(x, z); int color = event.rand.nextInt(16); - if(event.world.isAirBlock(x, y, z) && ModBlocks.mushroom.canBlockStay(event.world, x, y, z)) - event.world.setBlock(x, y, z, ModBlocks.mushroom, color, 2); + boolean primus = event.rand.nextInt(380) == 0; + + for(int j = 0; j < ConfigHandler.flowerDensity * ConfigHandler.flowerPatchChance; j++) { + int x1 = x + event.rand.nextInt(dist * 2) - dist; + int y1 = y + event.rand.nextInt(4) - event.rand.nextInt(4); + int z1 = z + event.rand.nextInt(dist * 2) - dist; + + if(event.world.isAirBlock(x1, y1, z1) && (!event.world.provider.hasNoSky || y1 < 127) && ModBlocks.flower.canBlockStay(event.world, x1, y1, z1)) { + if(primus) { + event.world.setBlock(x1, y1, z1, ModBlocks.specialFlower, 0, 2); + TileSpecialFlower flower = (TileSpecialFlower) event.world.getTileEntity(x1, y1, z1); + flower.setSubTile(event.rand.nextBoolean() ? LibBlockNames.SUBTILE_NIGHTSHADE_PRIME : LibBlockNames.SUBTILE_DAYBLOOM_PRIME); + SubTileDaybloom subtile = (SubTileDaybloom) flower.getSubTile(); + subtile.setPrimusPosition(); + } else { + event.world.setBlock(x1, y1, z1, ModBlocks.flower, color, 2); + if(event.rand.nextDouble() < ConfigHandler.flowerTallChance && ((BlockModFlower) ModBlocks.flower).func_149851_a(event.world, x1, y1, z1, false)) + BlockModFlower.placeDoubleFlower(event.world, x1, y1, z1, color, 0); + } + } + } } } } + + private void generateMushrooms(DecorateBiomeEvent.Decorate event) { + for(int i = 0; i < ConfigHandler.mushroomQuantity; i++) { + int x = event.chunkX + event.rand.nextInt(16) + 8; + int z = event.chunkZ + event.rand.nextInt(16) + 8; + int y = event.rand.nextInt(26) + 4; + + int color = event.rand.nextInt(16); + if(event.world.isAirBlock(x, y, z) && ModBlocks.mushroom.canBlockStay(event.world, x, y, z)) + event.world.setBlock(x, y, z, ModBlocks.mushroom, color, 2); + } + } } \ No newline at end of file diff --git a/src/main/java/vazkii/botania/common/core/handler/ConfigHandler.java b/src/main/java/vazkii/botania/common/core/handler/ConfigHandler.java index 03e032ac49..ef734af58f 100644 --- a/src/main/java/vazkii/botania/common/core/handler/ConfigHandler.java +++ b/src/main/java/vazkii/botania/common/core/handler/ConfigHandler.java @@ -101,6 +101,11 @@ public final class ConfigHandler { public static double flowerTallChance = 0.05; public static int mushroomQuantity = 40; + public static int[] flowerDimensionWhitelist = new int[]{}; + public static int[] flowerDimensionBlacklist = new int[]{}; + public static int[] mushroomDimensionWhitelist = new int[]{}; + public static int[] mushroomDimensionBlacklist = new int[]{}; + private static boolean verifiedPotionArray = false; private static int potionArrayLimit = 0; @@ -286,6 +291,18 @@ public static void load() { desc = "Enables all built-in recipes. This can be false for expert modpacks that wish to supply their own."; enableDefaultRecipes = loadPropBool("recipes.enabled", desc, enableDefaultRecipes); + desc = "Whitelist of which dimension generates Botania flowers. Empty means any dimension can."; + flowerDimensionWhitelist = loadPropIntArray("worldgen.flower.dimensionWhitelist", desc, flowerDimensionWhitelist); + + desc = "Blacklist of which dimension generates Botania flowers."; + flowerDimensionBlacklist = loadPropIntArray("worldgen.flower.dimensionBlacklist", desc, flowerDimensionBlacklist); + + desc = "Whitelist of which dimension generates Botania mushrooms. Empty means any dimension can."; + mushroomDimensionWhitelist = loadPropIntArray("worldgen.mushroom.dimensionWhitelist", desc, mushroomDimensionWhitelist); + + desc = "Blacklist of which dimension generates Botania mushrooms."; + mushroomDimensionBlacklist = loadPropIntArray("worldgen.mushroom.dimensionBlacklist", desc, mushroomDimensionBlacklist); + potionIDSoulCross = loadPropPotionId(LibPotionNames.SOUL_CROSS, potionIDSoulCross); potionIDFeatherfeet = loadPropPotionId(LibPotionNames.FEATHER_FEET, potionIDFeatherfeet); potionIDEmptiness = loadPropPotionId(LibPotionNames.EMPTINESS, potionIDEmptiness); @@ -334,6 +351,16 @@ public static boolean loadPropBool(String propName, String desc, boolean default return prop.getBoolean(default_); } + public static int[] loadPropIntArray(String propName, String desc, int[] intArray) { + Property prop = config.get(Configuration.CATEGORY_GENERAL, propName, intArray); + prop.comment = desc; + + if(adaptor != null) + adaptor.adaptPropertyIntArray(prop, prop.getIntList()); + + return prop.getIntList(); + } + public static int loadPropPotionId(String propName, int default_) { if(!verifiedPotionArray) verifyPotionArray(); @@ -455,6 +482,10 @@ public void adaptPropertyBool(Property prop, boolean val) { this.adaptProperty(prop, val); } + public void adaptPropertyIntArray(Property prop, int[] val) { + this.adaptProperty(prop, val); + } + public static class AdaptableValue { public final int version;