From 29ea58460f38e600f33c211a165e87366eb9e233 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Fri, 2 Jul 2021 02:40:41 -0400 Subject: [PATCH 01/37] Start of work on porting this to Minecraft 1.16 Not much done yet and this is more a rewrite thanks to the amount of change in Forge and Minecraft --- CHANGELOG.txt | 9 +- Jenkinsfile | 96 ++ LICENSE | 292 ++-- README.md | 181 +- build.gradle | 1527 +++++++++++++---- changelog.mustache | 10 + config/checkstyle/checkstyle-6.xml | 96 ++ config/checkstyle/checkstyle.xml | 96 ++ config/checkstyle/pmd-5.8.xml | 483 ++++++ config/checkstyle/pmd.xml | 334 ++++ config/pmd/pmd-5.1.xml | 468 +++++ config/pmd/pmd-5.8.xml | 483 ++++++ config/pmd/pmd.xml | 334 ++++ config/xsl/checkstyle-author.xsl | 224 +++ .../checkstyle-noframes-severity-sorted.xsl | 202 +++ config/xsl/checkstyle-noframes-sorted.xsl | 172 ++ config/xsl/checkstyle-noframes.xsl | 171 ++ gradle.properties | 150 +- gradle/wrapper/gradle-wrapper.jar | Bin 54413 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 55 +- gradlew.bat | 43 +- settings.gradle | 36 +- sonar-project.properties | 20 + .../com/mcmoddev/orespawn/EventHandlers.java | 259 --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 186 +- .../mcmoddev/orespawn/api/BiomeLocation.java | 20 - .../mcmoddev/orespawn/api/FeatureBase.java | 314 ---- .../com/mcmoddev/orespawn/api/IBlockList.java | 20 - .../mcmoddev/orespawn/api/IDimensionList.java | 12 - .../com/mcmoddev/orespawn/api/IFeature.java | 22 - .../com/mcmoddev/orespawn/api/OS3API.java | 177 ++ .../com/mcmoddev/orespawn/api/OS4Feature.java | 5 + .../exceptions/BadStateValueException.java | 13 - .../api/exceptions/BadValueException.java | 23 - .../exceptions/MissingVersionException.java | 10 - .../exceptions/NotAProperConfigException.java | 10 - .../api/exceptions/OldVersionException.java | 10 - .../api/exceptions/UnknownFieldException.java | 20 - .../api/exceptions/UnknownNameException.java | 23 - .../exceptions/UnknownVersionException.java | 10 - .../orespawn/api/os3/IBiomeBuilder.java | 25 - .../orespawn/api/os3/IBlockBuilder.java | 159 -- .../orespawn/api/os3/IBlockDefinition.java | 14 - .../orespawn/api/os3/IDimensionBuilder.java | 18 - .../orespawn/api/os3/IFeatureBuilder.java | 29 - .../orespawn/api/os3/IFeatureEntry.java | 21 - .../orespawn/api/os3/IReplacementBuilder.java | 82 - .../orespawn/api/os3/IReplacementEntry.java | 13 - .../orespawn/api/os3/ISpawnBuilder.java | 63 - .../orespawn/api/os3/ISpawnEntry.java | 46 - .../com/mcmoddev/orespawn/api/os3/OS3API.java | 71 - .../orespawn/api/os3/OS3FeatureGenerator.java | 6 - .../api/os3/OreSpawnBlockMatcher.java | 52 - .../orespawn/api/os3/package-info.java | 2 - .../OreSpawnPlugin.java => os3plugin.java} | 4 +- .../orespawn/api/plugin/IOreSpawnPlugin.java | 8 - .../orespawn/api/plugin/PluginLoader.java | 144 +- .../orespawn/commands/AddOreCommand.java | 118 -- .../orespawn/commands/ClearChunkCommand.java | 161 -- .../orespawn/commands/DumpBiomesCommand.java | 62 - .../commands/WriteConfigsCommand.java | 31 - .../com/mcmoddev/orespawn/data/Config.java | 165 +- .../com/mcmoddev/orespawn/data/Constants.java | 102 +- .../orespawn/data/FeatureRegistry.java | 162 -- .../mcmoddev/orespawn/data/OS4BlockData.java | 24 + .../orespawn/data/PresetsStorage.java | 95 - .../orespawn/data/ReplacementsRegistry.java | 265 --- .../mcmoddev/orespawn/data/VanillaOres.java | 16 - .../impl/features/ClusterGenerator.java | 214 --- .../features/DefaultFeatureGenerator.java | 136 -- .../impl/features/NormalCloudGenerator.java | 182 -- .../impl/features/PrecisionGenerator.java | 294 ---- .../orespawn/impl/features/UnderFluid.java | 212 --- .../orespawn/impl/features/VeinGenerator.java | 583 ------- .../impl/location/BiomeLocationAcceptAny.java | 25 - .../location/BiomeLocationComposition.java | 86 - .../location/BiomeLocationDictionary.java | 51 - .../impl/location/BiomeLocationEmpty.java | 21 - .../impl/location/BiomeLocationList.java | 60 - .../impl/location/BiomeLocationSingle.java | 50 - .../orespawn/impl/os3/BiomeBuilder.java | 99 -- .../orespawn/impl/os3/BlockBuilder.java | 188 -- .../orespawn/impl/os3/BlockDefinition.java | 33 - .../mcmoddev/orespawn/impl/os3/BlockList.java | 65 - .../orespawn/impl/os3/DimensionBuilder.java | 76 - .../orespawn/impl/os3/DimensionList.java | 59 - .../impl/os3/DimensionListAcceptAll.java | 21 - .../os3/DimensionListAcceptAllOverworld.java | 25 - .../impl/os3/DimensionListDenyAll.java | 22 - .../orespawn/impl/os3/FeatureBuilder.java | 122 -- .../orespawn/impl/os3/FeatureEntry.java | 53 - .../orespawn/impl/os3/OS3APIImpl.java | 282 --- .../orespawn/impl/os3/ReplacementBuilder.java | 113 -- .../orespawn/impl/os3/ReplacementEntry.java | 42 - .../orespawn/impl/os3/SpawnBuilder.java | 231 --- .../orespawn/impl/os3/SpawnEntry.java | 107 -- .../orespawn/json/OreSpawnReader.java | 447 ----- .../orespawn/json/OreSpawnWriter.java | 73 - .../orespawn/registries/FeaturesRegistry.java | 101 ++ .../orespawn/registries/IFeatureEntry.java | 11 + .../orespawn/registries/IPresetEntry.java | 10 + .../registries/IReplacementEntry.java | 17 + .../orespawn/registries/PresetsRegistry.java | 86 + .../registries/ReplacementsRegistry.java | 97 ++ .../mcmoddev/orespawn/util/Collectors2.java | 22 - .../com/mcmoddev/orespawn/util/StateUtil.java | 55 - .../com/mcmoddev/orespawn/utils/Helpers.java | 7 + .../mcmoddev/orespawn/utils/TypeUtils.java | 16 + .../orespawn/worldgen/FlatBedrock.java | 87 - .../worldgen/OreSpawnFeatureGenerator.java | 46 - .../orespawn/worldgen/OreSpawnWorldGen.java | 30 - src/main/resources/META-INF/mods.toml | 56 + .../assets/orespawn/configs/_features.json | 26 - .../orespawn/configs/_replacements.json | 8 - .../assets/orespawn/configs/orespawn.json | 332 ---- src/main/resources/mcmod.info | 15 - src/main/resources/pack.mcmeta | 7 +- update.json | 80 + 119 files changed, 5639 insertions(+), 8018 deletions(-) create mode 100644 Jenkinsfile create mode 100644 changelog.mustache create mode 100644 config/checkstyle/checkstyle-6.xml create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/pmd-5.8.xml create mode 100644 config/checkstyle/pmd.xml create mode 100644 config/pmd/pmd-5.1.xml create mode 100644 config/pmd/pmd-5.8.xml create mode 100644 config/pmd/pmd.xml create mode 100644 config/xsl/checkstyle-author.xsl create mode 100644 config/xsl/checkstyle-noframes-severity-sorted.xsl create mode 100644 config/xsl/checkstyle-noframes-sorted.xsl create mode 100644 config/xsl/checkstyle-noframes.xsl create mode 100644 sonar-project.properties delete mode 100644 src/main/java/com/mcmoddev/orespawn/EventHandlers.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/BiomeLocation.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/FeatureBase.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/IBlockList.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/IDimensionList.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/IFeature.java create mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS3API.java create mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java delete mode 100755 src/main/java/com/mcmoddev/orespawn/api/exceptions/BadStateValueException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/BadValueException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/MissingVersionException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/NotAProperConfigException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/OldVersionException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownFieldException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownNameException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownVersionException.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IBiomeBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IBlockBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IBlockDefinition.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IDimensionBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/OS3API.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/OS3FeatureGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/OreSpawnBlockMatcher.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/os3/package-info.java rename src/main/java/com/mcmoddev/orespawn/api/{plugin/OreSpawnPlugin.java => os3plugin.java} (87%) delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/plugin/IOreSpawnPlugin.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/commands/AddOreCommand.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/commands/ClearChunkCommand.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/commands/DumpBiomesCommand.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/commands/WriteConfigsCommand.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/FeatureRegistry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/PresetsStorage.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/ReplacementsRegistry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/VanillaOres.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/ClusterGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/DefaultFeatureGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/NormalCloudGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/PrecisionGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/UnderFluid.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/features/VeinGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationAcceptAny.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationComposition.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationDictionary.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationEmpty.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationList.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationSingle.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/BiomeBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/BlockBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/BlockDefinition.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/BlockList.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionList.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAll.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAllOverworld.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListDenyAll.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/OS3APIImpl.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnBuilder.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/json/OreSpawnReader.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/json/OreSpawnWriter.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/util/Collectors2.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/util/StateUtil.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Helpers.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/worldgen/FlatBedrock.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnFeatureGenerator.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnWorldGen.java create mode 100644 src/main/resources/META-INF/mods.toml delete mode 100644 src/main/resources/assets/orespawn/configs/_features.json delete mode 100644 src/main/resources/assets/orespawn/configs/_replacements.json delete mode 100755 src/main/resources/assets/orespawn/configs/orespawn.json delete mode 100644 src/main/resources/mcmod.info create mode 100644 update.json diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5bb9658..f5df02e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,8 +1 @@ -Version 3.3.1 - -* Cascading World Gen Lag issues that were known have been resolved -* Vein generator has been fixed to work properly -* Underfluid generator has been stubbed out and is in testing -* Various minor bugs fixed -* Some small performance improvements -* Most Current Release (something like build 169 or 170) fixes a logic error that saw the system refusing to spawn in any chunk with a negative X or Y coordinate. +Example Mod \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..76978fe --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,96 @@ +pipeline { + agent any + environment { + GRADLE_OPTS = '-Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dorg.gradle.warning.mode=all' +// JAVA_OPTS = '' + } + options { + ansiColor('xterm') + } + tools { +// git 'Git' + gradle 'Gradle 4.9' + jdk 'oraclejdk8' + } + stages { + stage('prebuild') { + steps { + sh 'rm -rf build/libs' + sh 'chmod +x gradlew' + sh 'java -version' + sh 'gradle -version' + sh './gradlew -version' + sh 'export' + } + } + stage('build') { + steps { + withGradle { + sh './gradlew clean build' + } + } + } + stage('test') { + steps { + withGradle { + sh './gradlew test' + } + } + } + stage('publish') { + steps { + withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { + withGradle { + sh './gradlew publish' + } + } + } + } + stage('CurseForge') { + steps { + withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { + withGradle { + sh './gradlew -x publish curseforge' + } + } + } + } + stage('SonarQube') { + tools { + jdk "oraclejdk11" + } + environment { + scannerHome = tool 'SonarQube' + } + steps { +// withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { +// withGradle { +// sh './gradlew sonarqube' +// } +// } + withSonarQubeEnv(installationName: 'SonarCloud', , envOnly: false) { + sh "${scannerHome}/bin/sonar-scanner -Dsonar.java.jdkHome=${JAVA_HOME}" + } + } + } + stage('postbuild') { + steps { + archiveArtifacts artifacts: 'build/libs/*.jar', followSymlinks: false + javadoc javadocDir: 'build/docs/javadoc', keepAll: false + fingerprint 'build/libs/*.zip' + junit allowEmptyResults: true, testResults: '**/build/test-results/junit-platform/*.xml' + jacoco classPattern: '**/build/classes/java', execPattern: '**/build/jacoco/**.exec', sourceInclusionPattern: '**/*.java', sourcePattern: '**/src/main/java' + findBuildScans() +// recordIssues(tools: [errorProne(pattern: 'ReportFilePattern', reportEncoding: 'UTF-8')]) + recordIssues(tools: [java()]) + recordIssues(tools: [javaDoc()]) + recordIssues(tools: [checkStyle(pattern: '**/build/reports/checkstyle/*.xml')]) + recordIssues(tools: [pmdParser(pattern: '**/build/reports/pmd/*.xml')]) +// recordIssues(tools: [findBugs(pattern: '*/build/reports/findbugs/*.xml', useRankAsPriority: true)]) + recordIssues(tools: [spotBugs(pattern: '**/build/reports/spotbugs/*.xml', useRankAsPriority: true)]) + recordIssues(tools: [junitParser(pattern: '**/build/test-results/junit-platform/*.xml')]) +// recordIssues(tools: [sonarQube(pattern: '**/sonar-report.json')]) + } + } + } +} diff --git a/LICENSE b/LICENSE index e62ad12..19e3071 100644 --- a/LICENSE +++ b/LICENSE @@ -1,32 +1,30 @@ -GNU Lesser General Public License -================================= + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 -_Version 2.1, February 1999_ -_Copyright © 1991, 1999 Free Software Foundation, Inc._ -_51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA_ + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) -_This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence -the version number 2.1._ + Preamble -### Preamble - -The licenses for most software are designed to take away your + The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. -This license, the Lesser General Public License, applies to some + This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. -When we speak of free software, we are referring to freedom of use, + When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get @@ -34,12 +32,12 @@ it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. -To protect your rights, we need to make restrictions that forbid + To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. -For example, if you distribute copies of the library, whether gratis + For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide @@ -47,32 +45,32 @@ complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. -We protect your rights with a two-step method: **(1)** we copyright the -library, and **(2)** we offer you this license, which gives you legal + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. -To protect each distributor, we want to make it very clear that + To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. -Finally, software patents pose a constant threat to the existence of + Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. -Most GNU software, including some libraries, is covered by the + Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. -When a program is linked with a library, whether statically or using + When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the @@ -80,7 +78,7 @@ entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. -We call this license the “Lesser” General Public License because it + We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages @@ -88,7 +86,7 @@ are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. -For example, on rare occasions, there may be a special need to + For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free @@ -96,51 +94,52 @@ library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. -In other cases, permission to use a particular library in non-free + In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. -Although the Lesser General Public License is Less protective of the + Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. -The precise terms and conditions for copying, distribution and + The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a -“work based on the library” and a “work that uses the library”. The +"work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. -### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -**0.** This License Agreement applies to any software library or other + 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called “this License”). -Each licensee is addressed as “you”. +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". -A “library” means a collection of software functions and/or data + A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. -The “Library”, below, refers to any such software library or work -which has been distributed under these terms. A “work based on the -Library” means either the Library or any derivative work under + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term “modification”.) +included without limitation in the term "modification".) -“Source code” for a work means the preferred form of the work for + "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. -Activities other than copying, distribution and modification are not + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based @@ -148,7 +147,7 @@ on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. -**1.** You may copy and distribute verbatim copies of the Library's + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact @@ -156,33 +155,37 @@ all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. -You may charge a fee for the physical act of transferring a copy, + You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. -**2.** You may modify your copy or copies of the Library or any portion + 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: -* **a)** The modified work must itself be a software library. -* **b)** You must cause the files modified to carry prominent notices -stating that you changed the files and the date of any change. -* **c)** You must cause the whole of the work to be licensed at no -charge to all third parties under the terms of this License. -* **d)** If a facility in the modified Library refers to a function or a -table of data to be supplied by an application program that uses -the facility, other than as an argument passed when the facility -is invoked, then you must make a good faith effort to ensure that, -in the event an application does not supply such function or -table, the facility still operates, and performs whatever part of -its purpose remains meaningful. -(For example, a function in a library to compute square roots has -a purpose that is entirely well-defined independent of the -application. Therefore, Subsection 2d requires that any -application-supplied function or table used by this function must -be optional: if the application does not supply it, the square -root function must still compute square roots.) + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, @@ -205,7 +208,7 @@ with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. -**3.** You may opt to apply the terms of the ordinary GNU General Public + 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, @@ -214,65 +217,65 @@ ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. -Once this change is made in a given copy, it is irreversible for + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. -This option is useful when you wish to copy part of the code of + This option is useful when you wish to copy part of the code of the Library into a program that is not a library. -**4.** You may copy and distribute the Library (or a portion or + 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. -If distribution of object code is made by offering access to copy + If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. -**5.** A program that contains no derivative of any portion of the + 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or -linked with it, is called a “work that uses the Library”. Such a +linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. -However, linking a “work that uses the Library” with the Library + However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a “work that uses the -library”. The executable is therefore covered by this License. +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. -When a “work that uses the Library” uses material from a header file + When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. -If such an object file uses only numerical parameters, data + If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) -Otherwise, if the work is a derivative of the Library, you may + Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. -**6.** As an exception to the Sections above, you may also combine or -link a “work that uses the Library” with the Library to produce a + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. -You must give prominent notice with each copy of the work that the + You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the @@ -280,36 +283,40 @@ copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: -* **a)** Accompany the work with the complete corresponding -machine-readable source code for the Library including whatever -changes were used in the work (which must be distributed under -Sections 1 and 2 above); and, if the work is an executable linked -with the Library, with the complete machine-readable “work that -uses the Library”, as object code and/or source code, so that the -user can modify the Library and then relink to produce a modified -executable containing the modified Library. (It is understood -that the user who changes the contents of definitions files in the -Library will not necessarily be able to recompile the application -to use the modified definitions.) -* **b)** Use a suitable shared library mechanism for linking with the -Library. A suitable mechanism is one that (1) uses at run time a -copy of the library already present on the user's computer system, -rather than copying library functions into the executable, and (2) -will operate properly with a modified version of the library, if -the user installs one, as long as the modified version is -interface-compatible with the version that the work was made with. -* **c)** Accompany the work with a written offer, valid for at -least three years, to give the same user the materials -specified in Subsection 6a, above, for a charge no more -than the cost of performing this distribution. -* **d)** If distribution of the work is made by offering access to copy -from a designated place, offer equivalent access to copy the above -specified materials from the same place. -* **e)** Verify that the user has already received a copy of these -materials or that you have already sent this user a copy. - -For an executable, the required form of the “work that uses the -Library” must include any data and utility programs needed for + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major @@ -317,28 +324,29 @@ components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. -It may happen that this requirement contradicts the license + It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. -**7.** You may place library facilities that are a work based on the + 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: -* **a)** Accompany the combined library with a copy of the same work -based on the Library, uncombined with any other library -facilities. This must be distributed under the terms of the -Sections above. -* **b)** Give prominent notice with the combined library of the fact -that part of it is a work based on the Library, and explaining -where to find the accompanying uncombined form of the same work. + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. -**8.** You may not copy, modify, sublicense, link with, or distribute + 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your @@ -346,7 +354,7 @@ rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. -**9.** You are not required to accept this License, since you have not + 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by @@ -355,7 +363,7 @@ Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. -**10.** Each time you redistribute the Library (or any work based on the + 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further @@ -363,7 +371,7 @@ restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. -**11.** If, as a consequence of a court judgment or allegation of patent + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not @@ -394,7 +402,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. -**12.** If the distribution and/or use of the Library is restricted in + 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, @@ -402,20 +410,20 @@ so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. -**13.** The Free Software Foundation may publish revised and/or new + 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and -“any later version”, you have the option of following the terms and +"any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. -**14.** If you wish to incorporate parts of the Library into other free + 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free @@ -424,19 +432,19 @@ decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. -### NO WARRANTY + NO WARRANTY -**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR @@ -447,23 +455,23 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -_END OF TERMS AND CONDITIONS_ + END OF TERMS AND CONDITIONS -### How to Apply These Terms to Your New Libraries + How to Apply These Terms to Your New Libraries -If you develop a new library, and you want it to be of the greatest + If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). -To apply these terms, attach the following notices to the library. It is + To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the -“copyright” line and a pointer to where the full notice is found. +"copyright" line and a pointer to where the full notice is found. - - Copyright (C) + {description} + Copyright (C) {year} {fullname} This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -477,18 +485,20 @@ convey the exclusion of warranty; and each file should have at least the You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your -school, if any, to sign a “copyright disclaimer” for the library, if +school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. - , 1 April 1990 - Ty Coon, President of Vice + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice That's all there is to it! diff --git a/README.md b/README.md index 63a51fc..b0cdaaa 100644 --- a/README.md +++ b/README.md @@ -1,152 +1,37 @@ -[![](https://img.shields.io/badge/Discord-MMD-green.svg?style=flat&logo=Discord)](https://discord.mcmoddev.com) -[![](http://cf.way2muchnoise.eu/full_mmd-orespawn_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/mmd-orespawn) -[![](http://cf.way2muchnoise.eu/versions/Minecraft_mmd-orespawn_all.svg)](https://www.curseforge.com/minecraft/mc-mods/mmd-orespawn) -[![Build Status](https://ci.mcmoddev.com/job/OreSpawn/job/OreSpawn%201.12/badge/icon)](https://ci.mcmoddev.com/job/OreSpawn/job/OreSpawn%201.12/) +# ForgeModdingSkeleton +Skeletons for building Forge mods -# OreSpawn -Minecraft library mod that provides better control over the spawning of ores in Minecraft. -If you're looking for a place to report bugs for the mod that adds extra mobs, the "DangerZone", etc... go [to the site for that mod](http://www.orespawn.com/) as this is not it. +## TODO +1) Support Curse ChangeLog from String (Partially done) +2) Sonarqube & Jar Signing Support for Travis +3) Sonarqube & Jar Signing Support for CircleCI +4) Secret parser should support Gradle properties too +5) Fix JUnit3, JUnit4 & JUnit Vintage compatibility (Should be done) +6) Support for JUnit on gradle < 4.6 (Effectively done, Needs uncommented and tested) +7) Jar-in-Jar (Partially done) +8) ContainedDeps (Partially done) +9) Shade +10) Shadow +11) Make all gradle version specific bits gated (Mostly complete) +12) Auto-selection selection of FG version based on MC version +13) Auto-selection of Forge latest LB and RB versions +14) Auto-selection of Mappings -## How it works -Ore Spawn parses all of the .json files found in `orespawn` and adds ore generators to the game based on those files. The JSON structure looks like this: +## Known Caveats +1) Many versions hard-coded +2) Only supports the maven-publish plugin, not the old maven plugin which also supports SSH Pubkey authentication -```json -[ - { - "dimension": -1, - "ores": [ - { - "block": "minecraft:quartz_ore", - "size": 15, - "variation": 4, - "frequency": 7, - "min_height": 0, - "max_height": 128 - } - ] - }, - { - "ores": [ - { - "block": "minecraft:coal_ore", - "size": 25, - "variation": 12, - "frequency": 20, - "min_height": 0, - "max_height": 128 - }, - { - "block": "minecraft:iron_ore", - "size": 8, - "variation": 4, - "frequency": 20, - "min_height": 0, - "max_height": 64 - }, - { - "block": "minecraft:gold_ore", - "size": 8, - "variation": 2, - "frequency": 2, - "min_height": 0, - "max_height": 32 - }, - { - "block": "minecraft:diamond_ore", - "size": 6, - "variation": 3, - "frequency": 8, - "min_height": 0, - "max_height": 16 - }, - { - "block": "minecraft:lapis_ore", - "size": 5, - "variation": 2, - "frequency": 1, - "min_height": 0, - "max_height": 32 - }, - { - "block": "minecraft:emerald_ore", - "size": 1, - "variation": 0, - "frequency": 8, - "min_height": 4, - "max_height": 32, - "biomes": [ - "minecraft:extreme_hills", - "minecraft:smaller_extreme_hills" - ] - }, - { - "block": "minecraft:dirt", - "size": 112, - "variation": 50, - "frequency": 10, - "min_height": 0, - "max_height": 255 - }, - { - "block": "minecraft:gravel", - "size": 112, - "variation": 50, - "frequency": 8, - "min_height": 0, - "max_height": 255 - }, - { - "block": "minecraft:stone", - "state": "variant=granite", - "size": 112, - "variation": 50, - "frequency": 10, - "min_height": 0, - "max_height": 255 - }, - { - "block": "minecraft:stone", - "state": "variant=diorite", - "size": 112, - "variation": 50, - "frequency": 10, - "min_height": 0, - "max_height": 255 - }, - { - "block": "minecraft:stone", - "state": "variant=andesite", - "size": 112, - "variation": 50, - "frequency": 10, - "min_height": 0, - "max_height": 255 - } - ] - } -] -``` +## Script related TODO +1) Document secret.json (Partially done) +2) Finish off JUnit5 feature set (I think this is done now) +3) Make sure JaCoCo is integrated correctly with JUnit -### dimension -The number ID of a dimension. Don't specify any dimension to target all dimensions *that are not already specified*. -### ores -Array of JSON objects specifying ore generators for this dimension -### block -Text ID of a block (the same you would use in the /give command) -### state -The state of a block (typically used for colored blocks) -### size -The number of blocks to spawn. Unlike the default Minecraft world settings JSON, this is the actually number of blocks that will spawn. -### variation -How much to randomly vary the number of blocks spawned (I recommend making this value 50% of the *size* value) -### frequency -How often, per chunk, to attempt to spawn this ore block. This value can be a fraction less than 1. If this value is between 0 and 1, then not every chunk will have a spawn in it. For example, a frequency of 0.1 means that there will be one attempt to spawn the ore per 10 chunks. -### min_height -The lowest Y-coordinate that the ore is allowed to spawn at -### max_height -The highest Y-coordinate that the ore is allowed to spawn at -### biomes -If this array is not empty, then the biomes in which the ore will spawn is restricted to those specified by ID in this array. - -# API -Adding OreSpawn support to your mod is not hard. Look at `VanillaOreSpawn.java` for an example. +## Example Mod TODO +1) Recipes +2) Creative Tabs +3) Block with Multiple Textures +4) Entities +5) Networking +6) Capabilities +7) Multiblock structures +8) Worldgen: Ore and Structure generation diff --git a/build.gradle b/build.gradle index aa37f0e..aa59464 100644 --- a/build.gradle +++ b/build.gradle @@ -1,322 +1,1205 @@ -def corePlugin = '' - -buildscript { - repositories { - jcenter() -// maven { -// url 'http://maven.aliyun.com/nexus/content/groups/public/' -// } - -// maven { -// url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' -// } - maven { - name = 'forge' - url = 'http://files.minecraftforge.net/maven' - } - maven { - name = 'gradle' - url 'https://plugins.gradle.org/m2/' - } - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public' - } - } - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' - classpath 'gradle.plugin.com.matthewprenger:CurseGradle:1.4.0' - classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0' - } -} - -apply plugin: 'net.minecraftforge.gradle.forge' -apply plugin: 'com.matthewprenger.cursegradle' -apply plugin: 'maven-publish' -apply plugin: 'org.sonarqube' - -javadoc { - failOnError = false -} - -description = 'OreSpawn' -def mod_file = getModFile() -def mc_version = "1.12" -def short_version = getVersion("VERSION", mod_file) -version = mc_version + "-" + short_version -if (System.getenv().BUILD_NUMBER) { - version += '.' + System.getenv().BUILD_NUMBER -} -group = "com.mcmoddev" -sourceCompatibility = targetCompatibility = "1.8" - -class Secrets { - def data = null - - def getProperty(String key) { - return data ? data[key] : '' - } -} - -import groovy.json.JsonSlurper - -def secretFile -if (System.getenv().SECRET_FILE) { - secretFile = file System.getenv().SECRET_FILE -} else { - secretFile = file 'secret.json' -} - -project.ext.secret = new Secrets() -if (secretFile.exists()) { - secretFile.withReader { - project.ext.secret.data = new JsonSlurper().parse it - } -} - -minecraft { - version = '1.12.2-14.23.5.2768' - runDir = 'run' - mappings = 'stable_39' -// coreMod = '' - makeObfSourceJar = false - - replace '@FINGERPRINT@', project.findProperty('signSHA1') -} - -repositories { - maven { // Mantle, TCon, JEI - name 'DVS1 Maven FS' - url 'http://dvs1.progwml6.com/files/maven' - } - maven { // CCL, CCC, NEI - name 'Chickenbones Repo' - url 'http://chickenbones.net/maven/' - } - maven { // The One Probe - name 'tterrag' - url 'http://maven.tterrag.com/' - } - maven { - name 'MMD' - url 'https://maven.mcmoddev.com/' - } - maven { // MCMultipart - name 'amadornes' - url 'http://maven.amadornes.com/' - } - maven { // Tesla - name 'epoxide' - url 'http://maven.epoxide.org' - } - maven { // CraftTweaker, ZenScript - name 'jared maven' - url 'http://blamejared.com/maven' - } - maven { // IC2 - name 'industrialcraft' - url 'http://maven.ic2.player.to' - } - maven { // WAILA/HWYLA - name 'tehnut' - url 'http://tehnut.info/maven' - } - maven { // CoFH - name 'Covers Maven' - url 'http://maven.covers1624.net' - } - maven { - name = 'CurseForge' - url = 'https://minecraft.curseforge.com/api/maven/' - } - maven { - name 'opencomputers' - url 'http://maven.cil.li/' - } - maven { // Mekanism, TAIGA - name 'jitpack' - url 'https://jitpack.io' - } -} - -dependencies { - // None -} - -processResources { - inputs.property 'version', project.version - inputs.property 'mcversion', project.minecraft.version - - from (sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version': short_version, 'mcversion': project.minecraft.version - } - - from (sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } -} - -jar { - manifest { -// attributes 'FMLCorePluginContainsFMLMod': 'true' -// attributes 'FMLCorePlugin': corePlugin -// attributes 'FMLAT' : '' - } -} - -task apiJar(type: Jar, dependsOn: classes) { - classifier = 'api' - from sourceSets.main.allSource - exclude('com/mcmoddev/orespawn/impl/**') - exclude('com/mcmoddev/orespawn/impl/features/**') - exclude('com/mcmoddev/orespawn/json/**') - exclude('com/mcmoddev/orespawn/world/**') - exclude('com/mcmoddev/orespawn/commands/**') - exclude('com/mcmoddev/orespawn/data/**') - exclude('com/mcmoddev/orespawn/*.java') -} - -task devJar(type: Jar) { - classifier = 'dev' - from sourceSets.main.output -} - -task deobfJar(type: Jar) { - classifier = 'deobf' - from sourceSets.main.output -} - -task signJar(type: SignJar, dependsOn: reobfJar) { - - // Skips if the keyStore property is missing. - onlyIf { - project.hasProperty('keyStore') - } - - // findProperty allows us to reference the property without it existing. - // Using project.propName would cause the script to fail validation if - // the property did not exist. - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - inputFile = jar.archivePath - outputFile = jar.archivePath -} - -build.dependsOn signJar - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.getDestinationDir() -} - -artifacts { - archives apiJar - archives devJar - archives deobfJar - archives sourceJar - archives javadocJar -} - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.group - artifactId project.archivesBaseName - version project.version - from components.java - artifact sourceJar { - classifier 'sources' - } - artifact apiJar { - classifier 'api' - } - artifact devJar { - classifier 'dev' - } - artifact deobfJar { - classifier 'deobf' - } - artifact javadocJar { - classifier 'javadoc' - } - } - } - repositories { - maven { - credentials { - username secret.username - password secret.password - } - url secret.url - } - } -} - -curseforge { - apiKey = secret.curseforgeAPIKey - project { - id = "245586" - changelog = file("CHANGELOG.txt") - releaseType = "release" - def projName = "OreSpawn" - def displayVersion = getVersion("VERSION", mod_file) - if (System.getenv().BUILD_NUMBER) { - displayVersion += '.' + System.getenv().BUILD_NUMBER - } - mainArtifact(jar) { - displayName = "$project.description $displayVersion" -// relations { -// } - } - addArtifact(apiJar) { - displayName = "$project.description $displayVersion API" - } - addArtifact(sourceJar) { - displayName = "$project.description $displayVersion Sources" - } - addArtifact(deobfJar) { - displayName = "$project.description $displayVersion Development" - } - addArtifact(javadocJar) { - displayName = "$project.description $displayVersion Javadoc" - } - } -} - -sonarqube { - properties { - property 'sonar.host.url', secret.sonarHost - property 'sonar.organization', secret.sonarOrganization - property 'sonar.login', secret.sonarToken - property 'sonar.projectName', project.archivesBaseName - property 'sonar.projectKey', "$project.group:$project.archivesBaseName" - } -} - -String getModFile() { - String path = 'src/main/java/com/mcmoddev/orespawn/data/Constants.java' - return path -} - -String getVersion(String type, String mod_file) { - String major = '0' - String revision = '0' - String patch = '0' - String prefix = "public static final String $type = \"" - File file = file(mod_file) - file.eachLine { String s -> - s = s.trim() - if (s.startsWith(prefix)) { - s = s.substring(prefix.length(), s.length() - 2) - String[] pts = s.split("\\.") - - major = pts[0] - revision = pts[1] - patch = pts[2] - } - } - return "$major.$revision.$patch" -} +buildscript { + repositories { + mavenLocal() + mavenCentral() + maven { + name = 'jcenter' + url = 'https://jcenter.bintray.com/' + } + removeIf { it instanceof MavenArtifactRepository && it.url.host == 'files.minecraftforge.net' } + removeIf { it instanceof MavenArtifactRepository && it.url.host == 'maven.minecraftforge.net' } + maven { + name = 'forge' + url = 'https://maven.minecraftforge.net/' + } + maven { + name = 'gradle' + url 'https://plugins.gradle.org/m2/' + } + maven { + name = 'sonatype' + url = 'https://oss.sonatype.org/content/groups/public' + } + } + dependencies { + if (GradleVersion.current() >= GradleVersion.version('5.0')) { + classpath group: 'gradle.plugin.com.github.spotbugs.snom', name: 'spotbugs-gradle-plugin', version: '4.7.1' + } else if (GradleVersion.current() >= GradleVersion.version('4.7')) { + classpath group: 'gradle.plugin.com.github.spotbugs', name: 'spotbugs-gradle-plugin', version: '1.6.9' + } else if (GradleVersion.current() >= GradleVersion.version('4.0')) { + classpath group: 'gradle.plugin.com.github.spotbugs', name: 'spotbugs-gradle-plugin', version: '1.6.5' + } + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: project.fg_version, changing: true + if (GradleVersion.current() > GradleVersion.version('3.3')) { + classpath group: 'org.checkerframework', name: 'checkerframework-gradle-plugin', version: '0.5.22' +// } else { +// classpath group: 'com.jaredsburrows', name: 'gradle-checker-framework-plugin', version: '0.2.1' + } + } +} + +plugins { +//id 'com.gradle.build-scan' version '1.16' // Gradle 4.x or earlier +//id 'com.gradle.build-scan' version '3.6.3' // Gradle 5.x +//id 'com.jaredsburrows.checkerframework' version '0.2.1'// Gradle 3.3 or earlier +//id 'com.jaredsburrows.checkerframework' version '0.2.2'// Gradle 3.4/4.0 or later +//id 'com.github.spotbugs' version '4.7.1' // 1.6.5 for Gradle 4.0-4.6, 1.6.9 for Gradle 4.7+ + id 'com.matthewprenger.cursegradle' version '1.4.0' + id 'org.sonarqube' version '3.3' + id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.66' +//id 'org.junit.platform.gradle.plugin' version '1.2.0' // Gradle 3.5 through 4.5 + id 'jacoco' + id 'checkstyle' + id 'pmd' + id 'eclipse' + id 'idea' + id 'maven-publish' +} + +if (isFG1()) { + apply plugin: 'forge' +} else if (isFG2()) { + apply plugin: 'net.minecraftforge.gradle.forge' +} else if (isFG3()) { + apply plugin: 'net.minecraftforge.gradle' +} + +import org.gradle.util.GradleVersion +if (gradleNewerThan('3.4')) { +// apply plugin: 'org.checkerframework' +} else { +// apply plugin: 'com.jaredsburrows.checkerframework' +} + +if (gradleNewerThan('4.0')) { + apply plugin: 'com.github.spotbugs' +} else { + apply plugin: 'findbugs' +} + +//import net.minecraftforge.gradle.common.task.SignJar + +gradleEnterprise { + buildScan { + if (System.getenv('CI')) { + publishAlways() + } +// publishAlwaysIf(System.getenv('CI')) + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' + } +} + +project.afterEvaluate { + //final def junitPlatformTestTask = project.tasks.getByName('junitPlatformTest') + + // configure jacoco to analyze the junitPlatformTest task + jacoco { + // this tool version is compatible with + toolVersion = '0.8.3' // 0.8.3 uses ASM 7.0.4, 0.8.4 uses ASM 7.1, 0.8.6 uses ASM 8.0.1, 0.8.7 uses ASM 9.1 + // Not needed with new way + //if (gradleOlderThan('4.6')) { + //applyTo junitPlatformTestTask + //} + } + + // create junit platform jacoco task + project.task(type: JacocoReport, 'junitPlatformJacocoReport', + { + if (gradleNewerThan('5.0')) { + getSourceDirectories().from(files('./src/main')) + getClassDirectories().from(files("${project.buildDir}/classes/main")) + } else { + sourceDirectories = files('./src/main') + classDirectories = files("${project.buildDir}/classes/main") + } + // Not needed with new way + //if (gradleOlderThan('4.6')) { + // executionData junitPlatformTestTask + //} + }) +} + +// Only works with JUnit Platform Launcher 1.2.0 and earlier +if (gradleOlderThan('4.6')) { + junitPlatform { + // platformVersion '1.2.0' + // logManager 'org.apache.logging.log4j.jul.LogManager' + reportsDir file("${project.buildDir}/test-results/junit-platform") + // enableStandardTestTask true + // selectors (optional) + // filters (optional) + } +} + +if (gradleNewerThan('4.0')) { + spotbugs { + ignoreFailures = true + toolVersion = '4.2.3' // 3.1.7 uses ASM 6.2, 3.1.8 uses ASM 6.2.1 + effort = 'max' + reportLevel = 'low' + //includeFilter = file("$rootProject.projectDir/config/spotbugs-filter.xml") + } + +//tasks.withType(com.github.spotbugs.SpotBugsTask) { // Gradle 4.x and earlier + tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { // Gradle 5.x and later + reports { + if (isCi()) { + xml.enabled = true + html.enabled = false + } else { + xml.enabled = false + html.enabled = true + } + } + } +} else { + findbugs { + toolVersion = "3.0.1" // Required for Java 8 +// toolVersion = "2.0.3" // Required for Java ? +// toolVersion = "1.3.9" // Required for Java ? + ignoreFailures = true + effort = 'max' + reportLevel = 'low' + } +} + +checkstyle { + ignoreFailures = true + if (gradleNewerThan('2.7')) { + toolVersion = '8.43' + } else { + configFile = file("${project.projectDir}/config/checkstyle/checkstyle-6.xml") + toolVersion = '6.7' // 6.19, 6.2 - ~6.19/8.28 requires jdk7, 6.0 - 6.1.1 jdk6, 5.9 for jdk5 + } + showViolations = false +} + +pmd { + if (gradleNewerThan('2.1')) { + consoleOutput = false + } + ignoreFailures = true + sourceSets = [sourceSets.main, sourceSets.test] + reportsDir = file("$project.buildDir/reports/pmd") + if (gradleNewerThan('6.8')) { + rulesMinimumPriority = 5 + } else if (gradleNewerThan('2.8')) { + rulePriority = 5 + } + ruleSets = [ ] + if (gradleNewerThan('5.6')) { + ruleSetFiles = files('config/pmd/pmd.xml') + toolVersion = '6.36.0' + incrementalAnalysis = true + } else if (gradleNewerThan('2.4')) { + ruleSetFiles = files('config/pmd/pmd-5.8.xml') + toolVersion = '5.8.1' + } else { + ruleSetFiles = files('config/pmd/pmd-5.8.xml') + toolVersion = '5.1.3' + } +} + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.compilerArgs = [ + '-Xlint:deprecation', // deprecation, rawtypes, cast, unchecked, all + //'-Xdiags:verbose', + //'-Werror' + ] + compileJava.options.compilerArgs += '-proc:none' +} + +tasks.withType(Test) { + compileTestJava.options.compilerArgs += '-proc:none' +} + +javadoc { + failOnError = false +} + +test { + if (gradleNewerThan('4.6')) { + useJUnitPlatform() + /* + useJUnitPlatform { + includeTags 'fast', 'smoke & feature-a' + //excludeTags 'slow', 'ci' + includeEngines 'junit-jupiter' + //excludeEngines 'junit-vintage' + } + */ + } +} + +if (project.hasProperty('mod_version')) { + project.ext.short_version = project.mod_version +} else if (project.hasProperty('mod_version_file')) { + ext.short_version = getModVersion((project.hasProperty('mod_version_const') ? project.mc_version : 'VERSION'), project.mod_version_file) +} else { + project.ext.short_version = getModVersion((project.hasProperty('mod_version_const') ? project.mc_version : 'VERSION')) +} +version = (project.hasProperty('mc_version') ? project.mc_version : project.forge_mc_version) + '-' + short_version +project.ext.display_version = short_version + +if ((System.getenv('BUILD_NUMBER')) || (System.getenv('TRAVIS_BUILD_NUMBER')) || (System.getenv('CIRCLE_BUILD_NUM'))) { + version += '.' + buildNumber + display_version += '.' + buildNumber +} + +static String getBuildNumber() { + return System.getenv('BUILD_NUMBER') ?: System.getenv('TRAVIS_BUILD_NUMBER') ?: System.getenv('CIRCLE_BUILD_NUM') ?: '0' +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 +compileJava.sourceCompatibility = JavaVersion.VERSION_1_8 +compileJava.targetCompatibility = JavaVersion.VERSION_1_8 + +eclipse.project { + buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' + natures 'org.eclipse.buildship.core.gradleprojectnature' +} + +idea.module { + downloadJavadoc = true + //inheritOutputDirs = true +} + +class Secrets { + Map m_data = null + + final String getProperty(final String key) { + return (m_data && (m_data[key] != null)) ? m_data[key] : '' + } +} + +final def secretFile +if (System.getenv('SECRET_FILE')) { + secretFile = file System.getenv().SECRET_FILE +} else { + secretFile = file 'secret.json' +} + +import groovy.json.JsonSlurper +project.ext.secret = new Secrets() +if (secretFile.exists()) { + secretFile.withReader { + project.secret.m_data = new JsonSlurper().parse it + } +} else { + project.secret.m_data = new JsonSlurper().parseText( + '{ "username" : "' + (System.getenv('userMaven') ?: '') + '",' + + '"password" : "' + (System.getenv('authMaven') ?: '') + '",' + + '"url" : "' + (System.getenv('urlMaven') ?: '') + '",' + + '"curseforgeAPIKey" : "' + (System.getenv('curse_auth') ?: '00000000-0000-0000-0000-000000000000') + '",' + + '"sonarHost" : "' + (System.getenv('SONAR_HOST_URL') ?: 'https://sonarcloud.io') + '",' + + '"sonarOrganization" : "' + (System.getenv('SONAR_ORGANIZATION') ?: '') + '",' + + '"sonarToken" : "' + (System.getenv('SONAR_TOKEN') ?: 0) + '"}' + ) +} + +minecraft { + if (isFG3()) { + mappings channel: project.mcp_mappings_channel, version: project.mcp_mappings_version + } else { + version = project.forge_mc_version + '-' + project.forge_version + if ((project.forge_mc_version != "1.6.4") && (project.forge_mc_version != "1.7.2")) { + mappings = project.mcp_mappings_version + } + + if ((project.fg_version != '1.0-SNAPSHOT') && (project.fg_version != '1.1-SNAPSHOT')) { + if (project.hasProperty('runDir')) { + runDir = project.runDir + } else if (file('../run').exists()) { + runDir = '../run' + } else { + runDir = 'run' + } + } else { + if (project.hasProperty('runDir')) { + assetDir = project.runDir + } else if (file('../assets').exists()) { + assetDir = '../assets' + } else { + assetDir = 'eclipse/assets' + } + } + + replace '@MOD_VERSION@', project.short_version + + if (project.hasProperty('signSHA1') && getBooleanProperty('do_sign_jar') && !isFG1()) { + replace '@FINGERPRINT@', project.findProperty('signSHA1') + } else { + replace '@FINGERPRINT@', '' + } + } + + if (project.hasProperty('core_plugin')) { + clientJvmArgs += "-Dfml.coreMods.load=${project.core_plugin}" + serverJvmArgs += "-Dfml.coreMods.load=${project.core_plugin}" + } + + if (project.hasProperty('mc_username')) { + clientRunArgs += ['--username', project.mc_username] + if (project.hasProperty('mc_password')) { + clientRunArgs += ['--password', project.mc_password] + } + } + if (project.hasProperty('mc_uuid')) { + clientRunArgs += ['--uuid', project.mc_uuid] + } + + if (getBooleanProperty('mc_server_nogui')) { + serverRunArgs += 'nogui' + } + + if (getBooleanProperty('forge_do_not_backup')) { + serverJvmArgs += '-Dfml.doNotBackup=true' + clientJvmArgs += '-Dfml.doNotBackup=true' + } + + if (getBooleanProperty('forge_query_result_confirm')) { + serverJvmArgs += '-Dfml.queryResult=confirm' + } + + if (getBooleanProperty('log4j_skip_jansi')) { + serverJvmArgs += '-Dlog4j.skipJansi=true' + clientJvmArgs += '-Dlog4j.skipJansi=true' + } + + if (project.hasProperty('mc_resolution')) { + final def res = [ + '480p': [640, 480], + '576p': [640, 576], + '720p': [1280, 720], + '1080p': [1920, 1080], + 'custom': [project.findProperty('mc_custom_resolution_width'), project.findProperty('mc_custom_resolution_height')]] + final def choice = res?.get(project.mc_resolution) + if (choice != null) { + clientRunArgs += ['--width', choice[0]] + clientRunArgs += ['--height', choice[1]] + } + } + + if (project.hasProperty('client_jvm_args')) { + clientJvmArgs += project.client_jvm_args + } + if (project.hasProperty('server_jvm_args')) { + serverJvmArgs += project.server_jvm_args + } + + if (project.hasProperty('client_game_args')) { + clientRunArgs += project.client_game_args + } + if (project.hasProperty('server_game_args')) { + serverRunArgs += project.server_game_args + } + + if (isFG2()) { + makeObfSourceJar = getBooleanProperty('create_source_jar') + + useDepAts = getBooleanProperty('dep_has_ats') + } + + if (isFG3()) { + if (project.hasProperty('mod_at_file')) { + accessTransformer = file(findProperty('mod_at_file')) + } + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + workingDirectory project.file('run') + + taskName 'runClient' + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + mods { + examplemod { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + + taskName 'runServer' + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + mods { + examplemod { + source sourceSets.main + } + } + } + + data { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + + mods { + examplemod { + source sourceSets.main + } + } + } + } + } +} + +if (isFG3()) { + // Include resources generated by data generators. + sourceSets.main.resources { srcDir 'src/generated/resources' } +} + +repositories { + mavenLocal() + mavenCentral() + maven { + name = 'jcenter' + url = 'https://jcenter.bintray.com/' + } + removeIf { it instanceof MavenArtifactRepository && it.url.host == 'files.minecraftforge.net' } + removeIf { it instanceof MavenArtifactRepository && it.url.host == 'maven.minecraftforge.net' } + maven { + name = 'forge' + url = 'https://maven.minecraftforge.net/' + } + maven { + name 'MMD' + url 'https://maven.mcmoddev.com/' + } +} + +dependencies { + if (isFG3()) { + minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" + } + + testImplementation('org.junit.platform:junit-platform-launcher:1.7.+') // testCompile + testImplementation('org.junit.jupiter:junit-jupiter-api:5.7.+') // testCompile + testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.+') // testRuntime + if (gradleNewerThan('2.13')) { + testCompileOnly 'junit:junit:4.13.2' + } + testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.7.+' //testRuntime + testImplementation('org.mockito:mockito-core:2.+') // testCompile + testImplementation('org.mockito:mockito-junit-jupiter:2.+') { // testCompile + exclude group: 'org.junit.jupiter', module: 'junit-jupiter-engine' + } + if (gradleNewerThan('4.6')) { + annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' + } +} + +processResources { + if (!isFG3()) { + inputs.property 'version', project.short_version + inputs.property 'mcversion', project.minecraft.version + + from (sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + expand 'version': project.short_version, 'mcversion': project.minecraft.version + } + + from (sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } + + rename '(.+_at.cfg)', 'META-INF/$1' + } +} + +jar { + //from sourceSets.api.output + //from sourceSets.main.output + + manifest { + mainAttributes( + 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}", + 'Timestamp': System.currentTimeMillis(), + 'Specification-Title': project.modid, + 'Specification-Vendor': project.vendor, + 'Specification-Version': '1', + 'Implementation-Title': project.archivesBaseName, + 'Implementation-Version': project.version, + 'Implementation-Vendor' : project.vendor, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + ) + if (!isFG3()) { + if (project.hasProperty('core_plugin')) { + attributes 'FMLCorePluginContainsFMLMod': 'true' + attributes 'FMLCorePlugin': project.core_plugin + } + if (project.hasProperty('mod_at_file')) { + attributes 'FMLAT': project.mod_at_file + } + } + if (project.hasProperty('mod_contained_deps')) { + attributes 'ContainedDeps': project.mod_contained_deps + } + } +} + +if (!isFG3()) { +task apiJar(type: Jar) { + onlyIf { + getBooleanProperty('create_api_jar') + } + + description = 'Creates a JAR containing the API Classes.' + classifier = 'api' + from apiClasses + // from sourceSets.api.output + manifest { + mainAttributes( + 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:api", + 'Timestamp': System.currentTimeMillis(), + 'Specification-Title': project.modid, + 'Specification-Vendor': project.vendor, + 'Specification-Version': '1', + 'Implementation-Title': project.archivesBaseName + '-api', + 'Implementation-Version': project.version, + 'Implementation-Vendor' : project.vendor, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + ) + } +} +} + +task deobfJar(type: Jar) { + onlyIf { + getBooleanProperty('create_deobf_jar') + } + + description = 'Creates a JAR containing the non-obfuscated compiled code.' + classifier = 'deobf' + from sourceSets.main.output + manifest { + mainAttributes( + 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:deobf", + 'Timestamp': System.currentTimeMillis(), + 'Specification-Title': project.modid, + 'Specification-Vendor': project.vendor, + 'Specification-Version': '1', + 'Implementation-Title': project.archivesBaseName + '-deobf', + 'Implementation-Version': project.version, + 'Implementation-Vendor' : project.vendor, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + ) + if (project.hasProperty('mod_at_file')) { + attributes 'FMLAT': project.mod_at_file + } + if (project.hasProperty('core_plugin')) { + attributes 'FMLCorePlugin': project.core_plugin + } + } +} + +if (!isFG2()) { +task sourceJar(type: Jar, dependsOn: classes) { + onlyIf { + getBooleanProperty('create_source_jar') + } + + description = 'Creates a JAR containing the source code.' + classifier = 'sources' + from sourceSets.main.allSource + manifest { + mainAttributes( + 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:sources", + 'Timestamp': System.currentTimeMillis(), + 'Specification-Title': 'examplemod', + 'Specification-Vendor': 'mcmoddev', + 'Specification-Version': '1', + 'Implementation-Title': project.name + '-sources', + 'Implementation-Version': project.version, + 'Implementation-Vendor' :'mcmoddev', + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + ) + } +} +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + onlyIf { + getBooleanProperty('create_javadoc_jar') + } + + description = 'Creates a JAR containing the JavaDocs.' + classifier = 'javadoc' + from javadoc.getDestinationDir() + manifest.mainAttributes( + 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:javadoc", + 'Timestamp': System.currentTimeMillis(), + 'Specification-Title': project.modid, + 'Specification-Vendor': project.vendor, + 'Specification-Version': '1', + 'Implementation-Title': project.archivesBaseName + '-javadoc', + 'Implementation-Version': project.version, + 'Implementation-Vendor' : project.vendor, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + ) +} + +def signType +if (isFG1() || isFG5()) { + signType = Jar +} else { + signType = SignJar + + task signJar(type: SignJar, dependsOn: jar) { + onlyIf { + getBooleanProperty('do_sign_jar') && + project.hasProperty('keyStore') + } + + description = 'Sign the Main JAR' + inputFile = jar.archivePath + outputFile = jar.archivePath + + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + } + + if (!isFG3()) { + task signApiJar(type: SignJar) { + onlyIf { + getBooleanProperty('create_api_jar') && + getBooleanProperty('do_sign_jar') && + project.hasProperty('keyStore') + } + + description = 'Sign the API JAR' + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + + inputFile = apiJar.archivePath + outputFile = apiJar.archivePath + } + } + + task signDeobfJar(type: SignJar) { + onlyIf { + getBooleanProperty('create_deobf_jar') && + getBooleanProperty('do_sign_jar') && + project.hasProperty('keyStore') + } + + description = 'Sign the deobf JAR' + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + + inputFile = deobfJar.archivePath + outputFile = deobfJar.archivePath + } + + task signSourceJar(type: SignJar) { + onlyIf { + getBooleanProperty('create_source_jar') && + getBooleanProperty('do_sign_jar') && + project.hasProperty('keyStore') + } + + description = 'Sign the Sources JAR' + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + +// if (!isFG3()) { + inputFile = sourceJar.archivePath + outputFile = sourceJar.archivePath +// } else { +// inputFile = sourcesJar.archivePath +// outputFile = sourcesJar.archivePath +// } + } + + task signJavadocJar(type: SignJar) { + onlyIf { + getBooleanProperty('create_javadoc_jar') && + getBooleanProperty('do_sign_jar') && + project.hasProperty('keyStore') + } + + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + + inputFile = javadocJar.archivePath + outputFile = javadocJar.archivePath + } +} + +if (getBooleanProperty('do_sign_jar') && (!isFG1())) { + build.dependsOn signJar + if (getBooleanProperty('create_api_jar') && !isFG3()) { + build.dependsOn signApiJar + } + if (getBooleanProperty('create_deobf_jar')) { + build.dependsOn signDeobfJar + } + if (getBooleanProperty('create_source_jar')) { +// if (!isFG3()) { + build.dependsOn signSourceJar +// } else { +// build.dependsOn signSourcesJar +// } + } + if (getBooleanProperty('create_javadoc_jar')) { + build.dependsOn signJavadocJar + } +} + +// TODO Test this +// Example configuration to allow publishing using the maven-publish task +// This is the preferred method to reobfuscate your jar file +jar.finalizedBy('reobfJar') +// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing +publish.dependsOn('reobfJar') +//tasks.publish.dependsOn build +tasks.curseforge.dependsOn publish + +artifacts { + if (getBooleanProperty('create_api_jar') && !isFG3()) { + archives apiJar + } + if (getBooleanProperty('create_deobf_jar')) { + archives deobfJar + } + if (getBooleanProperty('create_source_jar')) { +// if (!isFG3()) { + archives sourceJar +// } else { +// archives sourcesJar +// } + } + if (getBooleanProperty('create_javadoc_jar')) { + archives javadocJar + } +} + +//TODO: From Forge MDK +//def reobfFile = file("$buildDir/reobfJar/output.jar") +//def reobfArtifact = artifacts.add('default', reobfFile) { +// type 'jar' +// builtBy 'reobfJar' +//} + +publishing { + publications { + mavenJava(MavenPublication) { + //TODO: From Forge MDK + //artifact jar + //artifact reobfArtifact + //artifact reobfJar + groupId project.group + artifactId project.archivesBaseName + version project.version + from components.java + if (getBooleanProperty('create_source_jar')) { +// if (!isFG3()) { + artifact sourceJar { + classifier 'sources' + } +// } else { +// artifact sourcesJar { +// classifier 'sources' +// } +// } + } + if (getBooleanProperty('create_api_jar') && !isFG3()) { + artifact apiJar { + classifier 'api' + } + } + if (getBooleanProperty('create_deobf_jar')) { + artifact deobfJar { + classifier 'deobf' + } + } + if (getBooleanProperty('create_javadoc_jar')) { + artifact javadocJar { + classifier 'javadoc' + } + } + if (getBooleanProperty('pom_information') && gradleNewerThan('4.8')) { + pom { + name = project.name + description = project.description + url = project.pom_url + packaging = 'jar' + //inceptionYear = '' + if (getBooleanProperty('pom_license_information')) { + licenses { + license { + name = project.pom_license_name + url = project.pom_license_url + distribution = 'repo' + comments = project.pom_comments + } + } + } + if (getBooleanProperty('pom_scm_information')) { + scm { + connection = project.pom_scm_connection + developerConnection = project.pom_scm_developer_connection + // tag + url = project.pom_scm_url + } + } + if (getBooleanProperty('pom_issue_information')) { + issueManagement { + system = project.pom_issue_system + url = project.pom_issue_url + } + } + if (getBooleanProperty('pom_ci_information')) { + ciManagement { + system = project.pom_ci_system + url = project.pom_ci_url + } + } + if (getBooleanProperty('pom_organisation_information')) { + organization { + name = project.pom_organization_name + url = project.pom_organization_url + } + } + // contributors, developers, distributionManagement + } + } + } + } + repositories { + maven { + if ((secret.username != '') && (secret.password != '')) { + credentials { + username secret.username + password secret.password + } + } + if (secret.url != '') { + url secret.url + } else { + url "file:///${project.buildDir}/repo" + //TODO: From Forge MDK + //url "file:///${project.projectDir}/mcmodsrepo" + } + } + } +} + +curseforge { + if (project.hasProperty('curseforge_project_id') && getBooleanProperty('curseforge_do_upload')) { + apiKey = secret.curseforgeAPIKey + project { + id = project.curseforge_project_id + if (project.hasProperty('curseforge_changelog_filename')) { + changelog = file(project.curseforge_changelog_filename) + } else { + changelog = '' + } + if (project.hasProperty('curseforge_changelog_type')) { + changelogType = project.curseforge_changelog_type + } + if ((project.hasProperty('curseforge_release_type')) && ((project.curseforge_release_type == 'alpha') || (project.curseforge_release_type == 'beta') || (project.curseforge_release_type == 'release'))) { + releaseType = project.curseforge_release_type + } else { + releaseType = 'alpha' + } + if (project.hasProperty('curseforge_versions')) { + final def versions = "${project.curseforge_versions}".split(', ') + versions.each { + addGameVersion "${it}" + } + } + mainArtifact(jar) { + if (getBooleanProperty('curseforge_use_custom_display_name')) { + displayName = "${project.name} ${project.display_version}" + } + if (project.hasProperty('curseforge_requirements') || project.hasProperty('curseforge_optionals') || project.hasProperty('curseforge_embeddeds') || project.hasProperty('curseforge_tools') || project.hasProperty('curseforge_incompatibles') || project.hasProperty('curseforge_includes')) { + relations { + if (project.hasProperty('curseforge_requirements')) { + final def requirements = "${project.curseforge_requirements}".split(', ') + requirements.each { + requiredDependency "${it}" + } + } + if (project.hasProperty('curseforge_optionals')) { + final def optionals = "${project.curseforge_optionals}".split(', ') + optionals.each { + optionalDependency "${it}" + } + } + if (project.hasProperty('curseforge_embeddeds')) { + final def embeddeds = "${project.curseforge_embeddeds}".split(', ') + embeddeds.each { + embeddedLibrary "${it}" + } + } + if (project.hasProperty('curseforge_tools')) { + final def tools = "${project.curseforge_tools}".split(', ') + tools.each { + tool "${it}" + } + } + if (project.hasProperty('curseforge_incompatibles')) { + final def incompatibles = "${project.curseforge_incompatibles}".split(', ') + incompatibles.each { + incompatible "${it}" + } + } + } + } + } + if (getBooleanProperty('create_api_jar') && !isFG3()) { + addArtifact(apiJar) { + if (getBooleanProperty('curseforge_use_custom_display_name')) { + displayName = "${project.name} ${project.display_version} API" + } + } + } + if (getBooleanProperty('create_source_jar')) { +// if (!isFG3()) { + addArtifact(sourceJar) { + if (getBooleanProperty('curseforge_use_custom_display_name')) { + displayName = "${project.name} ${project.display_version} Sources" + } + } +// } else { +// addArtifact(sourcesJar) { +// if (getBooleanProperty('curseforge_use_custom_display_name')) { +// displayName = "${project.name} ${project.display_version} Sources" +// } +// } +// } + } + if (getBooleanProperty('create_deobf_jar')) { + addArtifact(deobfJar) { + if (getBooleanProperty('curseforge_use_custom_display_name')) { + displayName = "${project.name} ${project.display_version} Development" + } + } + } + if (getBooleanProperty('create_javadoc_jar')) { + addArtifact(javadocJar) { + if (getBooleanProperty('curseforge_use_custom_display_name')) { + displayName = "${project.name} ${project.display_version} Javadoc" + } + } + } + } + options { + debug = getBooleanProperty('curseforge_debug') + javaVersionAutoDetect = true + detectNewerJava = false + javaIntegration = true + forgeGradleIntegration = true + } + } +} + +sonarqube { + properties { + if (project.hasProperty('sonar_host_url')) { + property 'sonar.host.url', project.sonar_host_url + } else if (secret.sonarHost != '') { + property 'sonar.host.url', secret.sonarHost + } else { + property 'sonar.host.url', 'https://sonarcloud.io' + } + + if (secret.sonarToken != '') { + property 'sonar.login', secret.sonarToken + } + + if (project.hasProperty('sonar_organisation')) { + property 'sonar.organization', project.sonar_organisation + } else if (secret.sonarOrganisation != '') { + property 'sonar.organization', secret.sonarOrganization + } else { + property 'sonar.organization', 'unset' + } + + if (project.hasProperty('sonar_project_key')) { + property 'sonar.projectKey', project.sonar_project_key + } + + if (project.hasProperty('sonar_project_name')) { + property 'sonar.projectName', project.sonar_project_name + } + + if (project.hasProperty('sonar_project_description')) { + property 'sonar.projectDescription', project.sonar_project_description + } + + if (project.hasProperty('sonar_project_version')) { + property 'sonar.projectVersion', project.sonar_project_version + } + + if (project.hasProperty('sonar_branch_name')) { + property 'sonar.branch.name', project.sonar_branch_name + } + + if (project.hasProperty('sonar_branch_target')) { + property 'sonar.branch.target', project.sonar_branch_target + } + + property 'sonar.junit.reportPaths', "${project.buildDir}/test-results/junit-platform/*.xml" + property 'sonar.jacoco.reportPaths', "${project.buildDir}/jacoco/junitPlatformTest.exec" + //property 'sonar.groovy.jacoco.reportPath', "" + } +} + +String getModFile() { + String path = '' + final FileTree tree = fileTree(dir: 'src/main/java') + tree.include '**/*.java' + tree.visit { final element -> + if (element.file.isFile()) { + element.file.eachLine { final String s -> + final String sTrim = s.trim() + if (sTrim.startsWith('@Mod(')) { + path = "src/main/java/${element.relativePath}" + } + } + } + } + return path +} + +String getModVersion(final String type) { + return getModVersion(type, modFile) +} + +import java.util.regex.Matcher +String getModVersion(final String type, final String modFile) { + if ((modFile != null) && (modFile != '')) { + final File file = file(modFile) + final def prefix = ~/^(?:\s*)(?:(?public|protected|private) )?(?:(?static) )?(?:(?final) )?String $type = "(?:(?\d*)\.)?(?:(?\d*)\.)?(?:(?[^."]*)\.?)(?[^"]*)";$/ + for (final String s in file.readLines()) { + final Matcher matcher = (s.trim() =~ prefix) + if (matcher.matches()) { + return "${matcher.group('major') ?: '0'}.${matcher.group('minor') ?: '0'}.${matcher.group('patch')}" + } + } + } + return '0.0.0' +} + +boolean isFG1() { + return (project.fg_version == '1.0-SNAPSHOT') || (project.fg_version == '1.1-SNAPSHOT') || (project.fg_version == '1.2-SNAPSHOT') +} + +boolean isFG2() { + return (project.fg_version == '2.0-SNAPSHOT') || (project.fg_version == '2.1-SNAPSHOT') || (project.fg_version == '2.2-SNAPSHOT') || (project.fg_version == '2.3-SNAPSHOT') +} + +boolean isFG3() { + return (!isFG1() && !isFG2()) +} + +boolean isFG5() { + return true +// return (!isFG1() && !isFG2()) +} + +static boolean gradleOlderThan(final String version) { + return GradleVersion.current() < GradleVersion.version(version) +} + +static boolean gradleNewerThan(final String version) { + return GradleVersion.current() >= GradleVersion.version(version) +} + +static boolean isCi() { + return System.getenv('CI') +} + +boolean getBooleanProperty(final String property) { + if (project.hasProperty(property)) { + return ((String) project.findProperty(property)).toBoolean() + } else { + return false + } +} + +if (gradleNewerThan('2.1')) { + task gitChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) { + file = new File('CHANGELOG.md') + gitHubIssuePattern = "nonada123" + templateContent = file('changelog.mustache').getText('UTF-8') + } +} + +Object findProperty(final String propertyName) { + return hasProperty(propertyName) ? property(propertyName) : null +} + +/* +checkerFramework { + checkers = [ +// 'org.checkerframework.checker.units.UnitsChecker', + 'org.checkerframework.checker.nullness.NullnessChecker' + ] +} +*/ \ No newline at end of file diff --git a/changelog.mustache b/changelog.mustache new file mode 100644 index 0000000..076ba59 --- /dev/null +++ b/changelog.mustache @@ -0,0 +1,10 @@ +{{#tags}} + {{#issues}} + {{#commits}} +**{{{messageTitle}}}** +{{#messageBodyItems}} + * {{.}} +{{/messageBodyItems}} + {{/commits}} + {{/issues}} +{{/tags}} diff --git a/config/checkstyle/checkstyle-6.xml b/config/checkstyle/checkstyle-6.xml new file mode 100644 index 0000000..589a7e4 --- /dev/null +++ b/config/checkstyle/checkstyle-6.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..55c1473 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/pmd-5.8.xml b/config/checkstyle/pmd-5.8.xml new file mode 100644 index 0000000..525914a --- /dev/null +++ b/config/checkstyle/pmd-5.8.xml @@ -0,0 +1,483 @@ + + + + + PMD Configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/pmd.xml b/config/checkstyle/pmd.xml new file mode 100644 index 0000000..7dda2be --- /dev/null +++ b/config/checkstyle/pmd.xml @@ -0,0 +1,334 @@ + + + + + PMD Configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/pmd/pmd-5.1.xml b/config/pmd/pmd-5.1.xml new file mode 100644 index 0000000..907dc94 --- /dev/null +++ b/config/pmd/pmd-5.1.xml @@ -0,0 +1,468 @@ + + + + + PMD Configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/pmd/pmd-5.8.xml b/config/pmd/pmd-5.8.xml new file mode 100644 index 0000000..525914a --- /dev/null +++ b/config/pmd/pmd-5.8.xml @@ -0,0 +1,483 @@ + + + + + PMD Configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/pmd/pmd.xml b/config/pmd/pmd.xml new file mode 100644 index 0000000..7dda2be --- /dev/null +++ b/config/pmd/pmd.xml @@ -0,0 +1,334 @@ + + + + + PMD Configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/xsl/checkstyle-author.xsl b/config/xsl/checkstyle-author.xsl new file mode 100644 index 0000000..a924bb1 --- /dev/null +++ b/config/xsl/checkstyle-author.xsl @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +

CheckStyle Audit

+
Designed for use with CheckStyle and Ant.
+
+ + + +
+ + + +
+ + + + + +

+

+ +


+ + +
+ +

Summary

+ + + + + + + + + + + + + + + +
FilesErrorsWarnings
+ + + + + +
+
+ +

Authors

+ + + + + + + + + + + + + + + + + + + + + + + + +
NameErrorsWarnings
+ + + +
+
+ + + + + + + +
+

File +
+ Author +

+ + + + + + + + + + + + + +
Error DescriptionLine
+ + + +
+
Back to top + + + + + + + + + substring-after($path, '\') + + + + + + + + + + ab + +
diff --git a/config/xsl/checkstyle-noframes-severity-sorted.xsl b/config/xsl/checkstyle-noframes-severity-sorted.xsl new file mode 100644 index 0000000..e83f50d --- /dev/null +++ b/config/xsl/checkstyle-noframes-severity-sorted.xsl @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

CheckStyle Audit

Designed for use with CheckStyle and Ant.
+
+ + + +
+ + + +
+ + + + +
+ + +
+ + +

Files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameErrorsWarningsInfos
+
+ + + +

File

+ + + + + + + + + + + + + + + + +
SeverityError DescriptionLine
+ Back to top +
+ + +

Summary

+ + + + + + + + + + + + + + + + + + +
FilesErrorsWarningsInfos
+
+ + + + a + b + + +
+ + diff --git a/config/xsl/checkstyle-noframes-sorted.xsl b/config/xsl/checkstyle-noframes-sorted.xsl new file mode 100644 index 0000000..dc6486e --- /dev/null +++ b/config/xsl/checkstyle-noframes-sorted.xsl @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

CheckStyle Audit

Designed for use with CheckStyle and Ant.
+
+ + + +
+ + + +
+ + + + +
+ + +
+ + +

Files

+ + + + + + + + + + + + + + + +
NameErrors
+
+ + + +

File

+ + + + + + + + + + + + + + +
Error DescriptionLine
+ Back to top +
+ + +

Summary

+ + + + + + + + + + + + +
FilesErrors
+
+ + + + a + b + + +
+ + diff --git a/config/xsl/checkstyle-noframes.xsl b/config/xsl/checkstyle-noframes.xsl new file mode 100644 index 0000000..576a809 --- /dev/null +++ b/config/xsl/checkstyle-noframes.xsl @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

CheckStyle Audit

Designed for use with CheckStyle and Ant.
+
+ + + +
+ + + +
+ + + + + +

+

+ + +


+ + +
+ + +

Files

+ + + + + + + + + + + + + + +
NameErrors
+
+ + + +

File

+ + + + + + + + + + + + + +
Error DescriptionLine
+ Back to top +
+ + +

Summary

+ + + + + + + + + + + + +
FilesErrors
+
+ + + + a + b + + +
diff --git a/gradle.properties b/gradle.properties index 3fb922e..4213929 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,24 +1,130 @@ +org.gradle.daemon = false + +group = com.mcmoddev archivesBaseName = OreSpawn +modid = orespawn +vendor = mcmoddev +name = MMD OreSpawn +description = Spawn Your ORes! + +#Minumum Minecraft Version Mod will be built with +mc_version=1.16.5 + +#ForgeGradle version to use (Required) +fg_version = 5.+ +#version of Minecraft Forge is built against (Required) +forge_mc_version = 1.16.5 +#Forge version to use (Required) +forge_version = 36.1.32 +#Mappings channel: snapshot, stable or official +mcp_mappings_channel = snapshot +#Mappings version to use (Required) +mcp_mappings_version = 20210309-1.16.5 + +# The following will currently only be set when using Gradle 4.8 or later +pom_information = true +pom_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn' + +pom_license_information = true +pom_license_name = 'GNU Lesser General Public License v2.1' +pom_license_url = 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt' +pom_comments = '' + +pom_scm_information = true +pom_scm_connection = 'scm:git:git://github.com/MinecraftModDevelopmentMods/OreSpawn.git' +pom_scm_developer_connection = 'scm:git:git@github.com:MinecraftModDevelopmentMods/OreSpawn.git' +pom_scm_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn' + +pom_issue_information = true +pom_issue_system = 'github' +pom_issue_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn/issues' + +pom_ci_information = true +pom_ci_system = 'jenkins' +pom_ci_url = 'https://ci.mcmoddev.com/' + +pom_organisation_information = true +pom_organization_name = 'Minecraft Mod Development' +pom_organization_url = 'https://mcmoddev.com/' + +#curseforge_do_upload=false +#curseforge_debug=false +#Cursforge Project ID (Required) +#curseforge_project_id = 0 +#Type of Release, can be one of 'alpha', 'beta' or 'release' (Defaults to 'alpha' if not set) +#curseforge_release_type = alpha +#When using a ChangeLog file specifies filename +#curseforge_changelog_filename = CHANGELOG.txt +#Changelog type, can be one of 'text', 'html' or 'markdown' (Defaults to 'text' if not set) +#curseforge_changelog_type = text +#Version(s) of Minecraft this mod will work on (comma separated list) +#curseforge_versions = 1.16.5 +#Whether to use a custom display name on artifacts +#curseforge_use_custom_display_name = true +#List of required dependencies (comma separated list) +#curseforge_requirements = +#List of optional dependencies (comma separated list) +#curseforge_optionals = +#List of embedded dependencies (comma separated list) +#curseforge_embeddeds = +#List of compatible tools (comma separated list) +#curseforge_tools = +#List of incompatible dependencies (comma separated list) +#curseforge_incompatibles = +#List of included dependencies (comma separated list) +#curseforge_includes = + +#String reference to Core Plugin this mod contains, if any +#core_plugin = +#Whether or not to use Access Transformers from depended mods +#dep_has_ats = false +#File name of this mod's Access Transformers (If any) +#mod_at_file = _at.cfg + +#Whether the artifacts should be signed +do_sign_jar = false + +#Create a source jar? +create_source_jar = true +#Create an API jar? +create_api_jar = false +#Create a deobf jar? +create_deobf_jar = false +#Create a javadoc jar? +create_javadoc_jar = true + +#The following four options are optional and do not affect your build in any way +#They are only for convenience when using the 'runClient' and 'runServer' tasks +#Whether to Disable server gui +#mc_server_nogui = true +#Whether to Skip the screen to confirm that you want to load a world with missing registry entries +#forge_do_not_backup = true +#Whether to skip having to confirm on server +#forge_query_result_confirm = true +#Whether to Skip jansi warnings in the log +#log4j_skip_jansi = true + +# Optional convenience setter for game resolution one of '480p', '576p', '720p', '1080p' or 'custom' +#mc_resolution = 1080p +#the following two will only be used when mc_resolution = custom +#mc_custom_resolution_width = 1920 +#mc_custom_resolution_height = 1080 +#mc_fullscreen = true + +#Additional arguments to pass to minecraft.clientJvmArgs +#client_jvm_args = +#Additional arguments to pass to minecraft.serverJvmArgs +#client_game_args = +#Additional arguments to pass to minecraft.clientRunArgs +#server_jvm_args = +#Additional arguments to pass to minecraft.serverRunArgs +#server_game_args = -mc_version=1.12 -top_mc_version=1.11 -ccl_version=2.4.3.165 -ccc_version=2.3.5.93 -nei_version=2.1.2.175 -//jei_version=3.10.0.268 -//jei_version=3.12.8.328 -//jei_version=3.13.6.389 -jei_version=+ -mantle_version=1.1.3.199 -tconstruct_version=2.6.1.464 -mcmp_version=1.2.1 -//mcmp_version=1.3.0 -mcmp_experimental_version=2.0.0_88 -top_version=1.3.3-46 -orespawn_version=3.1.0+ -tesla_version=1.2.1.50 -bme_version=2.5.0-beta1 -mme_version=0.11.0 -fme_version=0.11.0 -bmi_version=0.11.0 -pa_version=2.3.0 \ No newline at end of file +##The following three options should *NEVER* be set in this file, it's a security risk, Be safe, keep your account information private. +##These properties here are for reference only and explanation of what they do +##Your Minecraft account username (Consistent player name for singleplayer worlds) +##mc_username = +##Your Minecraft account password (Allows online play while present with associated username) +##mc_password = +##Your Minecraft account uuid (Allows using your skin while testing, This can be with or without hyphens) +##mc_uuid = diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a9516871afd710a9d84d89e31ba77745607bd..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 54413 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a95009c..69a9715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..744e882 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -56,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle index 0e64f4a..93a3a1e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,36 @@ +// Gradle 6.x or later +plugins { + id 'com.gradle.enterprise' version '3.6.3' +} + +// Workaround to make the JUnit Platform Gradle Plugin available using the `plugins` DSL +// See https://github.com/junit-team/junit5/issues/768 +if (!(GradleVersion.current() <= GradleVersion.version('2.14.1'))) { + pluginManagement { + repositories { + gradlePluginPortal() + maven { url = ArtifactRepositoryContainer.MAVEN_CENTRAL_URL } + } + resolutionStrategy { + eachPlugin { + if (requested.id.id == 'org.junit.platform.gradle.plugin') { + useModule("org.junit.platform:junit-platform-gradle-plugin:${requested.version}") + } + } + } + } +} + rootProject.name = archivesBaseName -enableFeaturePreview('STABLE_PUBLISHING') +import org.gradle.util.GradleVersion +if ((GradleVersion.current() >= GradleVersion.version('4.8')) && (GradleVersion.current() <= GradleVersion.version('4.10.3'))) { + enableFeaturePreview('STABLE_PUBLISHING') // 4.10.3 +} + +// TODO: Things which likely don't work with FG yet, but I've not checked. +/* +if ((GradleVersion.current() >= GradleVersion.version('4.6')) && (GradleVersion.current() <= GradleVersion.version('4.10.3'))) { + enableFeaturePreview('IMPROVED_POM_SUPPORT') // Previously -Dorg.gradle.advancedpomsupport=true + enableFeaturePreview('GRADLE_METADATA') // Previously -Dorg.gradle.gradlemetadata=true +} +*/ diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..b6ca5fe --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,20 @@ +sonar.projectKey=jriwanek_ForgeModdingSkeleton +sonar.organization=jriwanek-github + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=ForgeModdingSkeleton +#sonar.projectVersion=1.0 + +sonar.java.binaries=./build/classes + +# Encoding of the source code. Default is default system encoding +sonar.sourceEncoding=UTF-8 + +#sonar.coverageReportPaths= +#sonar.coverage.jacoco.xmlReportPaths= +#sonar.testExecutionReportPath= +sonar.junit.reportPaths=**/build/test-results/junit-platform/*.xml + +sonar.java.checkstyle.reportPaths=**/build/reports/checkstyle/*.xml +sonar.java.pmd.reportPaths=**/build/reports/pmd/*.xml +sonar.java.spotbugs.reportPaths=**/build/reports/spotbugs/*.xml diff --git a/src/main/java/com/mcmoddev/orespawn/EventHandlers.java b/src/main/java/com/mcmoddev/orespawn/EventHandlers.java deleted file mode 100644 index bcaaadf..0000000 --- a/src/main/java/com/mcmoddev/orespawn/EventHandlers.java +++ /dev/null @@ -1,259 +0,0 @@ -package com.mcmoddev.orespawn; - -import java.util.Arrays; -import java.util.Deque; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.MapDifference; -import com.google.common.collect.Maps; -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.worldgen.OreSpawnFeatureGenerator; - -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.Tuple; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.gen.ChunkProviderServer; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.event.terraingen.OreGenEvent; -import net.minecraftforge.event.terraingen.OreGenEvent.GenerateMinable.EventType; -import net.minecraftforge.event.world.ChunkDataEvent; -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import net.minecraftforge.fml.common.eventhandler.Event; -import net.minecraftforge.fml.common.eventhandler.EventPriority; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; -import net.minecraftforge.fml.common.gameevent.TickEvent.WorldTickEvent; -import net.minecraftforge.fml.relauncher.Side; - -public class EventHandlers { - - private final Deque retroChunks; - private final Deque>> chunks; - // private Map> chunks - - EventHandlers() { - retroChunks = new ConcurrentLinkedDeque<>(); - chunks = new ConcurrentLinkedDeque<>(); - } - - private final List vanillaEvents = Arrays.asList(EventType.ANDESITE, EventType.COAL, - EventType.DIAMOND, EventType.DIORITE, EventType.DIRT, EventType.EMERALD, EventType.GOLD, - EventType.GRANITE, EventType.GRAVEL, EventType.IRON, EventType.LAPIS, - EventType.REDSTONE, EventType.QUARTZ, EventType.SILVERFISH); - - @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true) - public void onGenerateMinable(final OreGenEvent.GenerateMinable event) { - if (Config.getBoolean(Constants.REPLACE_VANILLA_OREGEN) - && vanillaEvents.contains(event.getType())) { - event.setResult(Event.Result.DENY); - } else if(Config.getBoolean(Constants.REPLACE_ALL) && !Loader.instance().activeModContainer().getModId().equals("orespawn")) { - event.setResult(Event.Result.DENY); - } - } - - @SubscribeEvent - public void onChunkSave(final ChunkDataEvent.Save ev) { - final NBTTagCompound dataTag = ev.getData().getCompoundTag(Constants.CHUNK_TAG_NAME); - final NBTTagCompound features = new NBTTagCompound(); - - // save a list of the spawns that were configured and available - in this dimension - when - // the chunk - // was first generated. - - // collect that data - final int thisDimension = ev.getWorld().provider.getDimension(); - final BlockPos thisPos = new BlockPos(ev.getChunk().x + 8, 128, ev.getChunk().z + 8); - final Biome thisBiome = ev.getChunk().getBiome(thisPos, ev.getWorld().getBiomeProvider()); - - OreSpawn.API.getAllSpawns().entrySet().stream() - .filter(ent -> ent.getValue().dimensionAllowed(thisDimension)) - .filter(ent -> ent.getValue().biomeAllowed(thisBiome)).forEach(ent -> features - .setString(ent.getKey(), ent.getValue().getFeature().getFeatureName())); - dataTag.setTag(Constants.FEATURES_TAG, features); - - ev.getData().setTag(Constants.CHUNK_TAG_NAME, dataTag); - } - - private boolean dequeContains(final ChunkPos cc) { - return chunks.stream().map(tup -> tup.getFirst().equals(cc)).collect(Collectors.toList()) - .contains(true); - } - - @SubscribeEvent - public void onChunkLoad(final ChunkDataEvent.Load ev) { - final World clWorld = ev.getWorld(); - final ChunkPos chunkCoords = new ChunkPos(ev.getChunk().x, ev.getChunk().z); - - doBedrockRetrogen(chunkCoords); - - if (dequeContains(chunkCoords)) { - return; - } - - if (Config.getBoolean(Constants.RETROGEN_KEY)) { - final NBTTagCompound chunkTag = ev.getData().getCompoundTag(Constants.CHUNK_TAG_NAME); - final int thisDimension = clWorld.provider.getDimension(); - final BlockPos thisPos = new BlockPos(ev.getChunk().x + 8, 128, ev.getChunk().z + 8); - final Biome thisBiome = ev.getChunk().getBiome(thisPos, clWorld.getBiomeProvider()); - - if (featuresAreDifferent(chunkTag, thisDimension, thisBiome) - || Config.getBoolean(Constants.FORCE_RETROGEN_KEY)) { - chunks.addLast(new Tuple<>(chunkCoords, - getDifferingTags(chunkTag, thisDimension, thisBiome))); - } - } - } - - private List getDifferingTags(final NBTTagCompound chunkTag, final int dim, - final Biome biome) { - final NBTTagCompound tagList = chunkTag.getCompoundTag(Constants.FEATURES_TAG); - final Map currentBits = new TreeMap<>(); - final Map oldBits = new TreeMap<>(); - - OreSpawn.API.getAllSpawns().entrySet().stream() - .filter(ent -> ent.getValue().dimensionAllowed(dim)) - .filter(ent -> ent.getValue().biomeAllowed(biome)).forEach(ent -> currentBits - .put(ent.getKey(), ent.getValue().getFeature().getFeatureName())); - - tagList.getKeySet().stream().forEach(tag -> oldBits.put(tag, tagList.getString(tag))); - - final MapDifference diff = Maps.difference(oldBits, currentBits); - - final List stuff = Lists.newLinkedList(); - stuff.addAll(diff.entriesDiffering().entrySet().stream().map(Map.Entry::getKey) - .collect(Collectors.toList())); - stuff.addAll(diff.entriesOnlyOnRight().entrySet().stream().map(Map.Entry::getKey) - .collect(Collectors.toList())); - return ImmutableList.copyOf(stuff); - } - - private boolean featuresAreDifferent(final NBTTagCompound chunkTag, final int dim, - final Biome biome) { - final NBTTagCompound tagList = chunkTag.getCompoundTag(Constants.FEATURES_TAG); - final Map currentBits = new TreeMap<>(); - final Map oldBits = new TreeMap<>(); - - OreSpawn.API.getAllSpawns().entrySet().stream() - .filter(ent -> ent.getValue().dimensionAllowed(dim)) - .filter(ent -> ent.getValue().biomeAllowed(biome)).forEach(ent -> currentBits - .put(ent.getKey(), ent.getValue().getFeature().getFeatureName())); - - tagList.getKeySet().stream().forEach(tag -> oldBits.put(tag, tagList.getString(tag))); - - final MapDifference diff = Maps.difference(oldBits, currentBits); - - return diff.entriesDiffering().size() == 0 && diff.entriesOnlyOnLeft().size() == 0 - && diff.entriesOnlyOnRight().size() == 0; - } - - private void doBedrockRetrogen(final ChunkPos chunkCoords) { - if (retroChunks.contains(chunkCoords)) { - return; - } - - if (Config.getBoolean(Constants.RETRO_BEDROCK)) { - retroChunks.addLast(chunkCoords); - } - } - - private static World world; - private static ChunkProviderServer chunkProvider; - private static IChunkGenerator chunkGenerator; - private static Random random; - - private static void setupData(final World nw) { - if (world == null || !world.equals(nw)) { - world = nw; - } - - if (chunkProvider == null || !chunkProvider.equals(nw.getChunkProvider())) { - chunkProvider = (ChunkProviderServer) nw.getChunkProvider(); - } - - if (chunkGenerator == null) { - chunkGenerator = ObfuscationReflectionHelper.getPrivateValue(ChunkProviderServer.class, - chunkProvider, "field_186029_c", "chunkGenerator"); - } - - if (random == null) { - random = new Random(world.getSeed()); - } - - } - - private OreSpawnFeatureGenerator findSpawn(final String spawnName) { - for (OreSpawnFeatureGenerator ofg : OreSpawn.API.getGenerators()) { - if (ofg.getSpawnName().matches(spawnName)) return ofg; - } - return null; - } - - private void runBits(final Tuple> tup, World world) { - final ChunkPos p = tup.getFirst(); - final List spawns = tup.getSecond(); - if(!world.isBlockLoaded(new BlockPos(p.getXStart(), 128, p.getZStart()))) { - chunks.add(tup); - return; - } - // re-seed with something totally new :P - random.setSeed((((random.nextLong() >> 4 + 1) + p.x) + ((random.nextLong() >> 2 + 1) + p.z)) - ^ world.getSeed()); - spawns.stream().forEach(s -> { - OreSpawnFeatureGenerator feature = findSpawn(s); - if (feature != null) feature.generate(random, p.x, p.z, world, chunkGenerator, chunkProvider); - }); - } - - @SubscribeEvent - public void worldTick(final WorldTickEvent ev) { - setupData(ev.world); - - if (ev.side != Side.SERVER) { - return; - } - - if (ev.phase == Phase.END) { - int chunkCount = chunks.size(); - for (int c = 0; c < 25 && !chunks.isEmpty(); c++) { - Tuple> pp = chunks.pop(); - if(ev.world.isBlockLoaded(new BlockPos(pp.getFirst().getXStart(), 128, pp.getFirst().getZStart()))) { - runBits(pp, ev.world); - } else { - chunks.add(pp); - c--; - } - chunkCount--; - if (chunkCount <= 0) { - break; - } - } - - chunkCount = retroChunks.size(); - for (int c = 0; c < 25 && !retroChunks.isEmpty(); c++) { - final ChunkPos p = retroChunks.pop(); - if(ev.world.isBlockLoaded(new BlockPos(p.getXStart(), 128, p.getZStart()))) { - OreSpawn.flatBedrock.retrogen(world, p.x, p.z); - } else { - c--; - retroChunks.add(p); - } - chunkCount--; - if (chunkCount <= 0) { - break; - } - } - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index f0c6bad..1da0d94 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,100 +1,106 @@ package com.mcmoddev.orespawn; +import net.minecraft.block.Block; +import net.minecraft.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.ModList; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; +import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.forgespi.language.ModFileScanData; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.objectweb.asm.Type; -import com.mcmoddev.orespawn.api.os3.OS3API; +import com.mcmoddev.orespawn.api.OS3API; +import com.mcmoddev.orespawn.api.os3plugin; import com.mcmoddev.orespawn.api.plugin.PluginLoader; -import com.mcmoddev.orespawn.commands.AddOreCommand; -import com.mcmoddev.orespawn.commands.ClearChunkCommand; -import com.mcmoddev.orespawn.commands.DumpBiomesCommand; -import com.mcmoddev.orespawn.commands.WriteConfigsCommand; -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.FeatureRegistry; -import com.mcmoddev.orespawn.impl.os3.OS3APIImpl; -import com.mcmoddev.orespawn.worldgen.FlatBedrock; -import com.mcmoddev.orespawn.worldgen.OreSpawnFeatureGenerator; +import com.mcmoddev.orespawn.utils.Helpers; +import com.mcmoddev.orespawn.utils.TypeUtils; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventHandler; -import net.minecraftforge.fml.common.Mod.Instance; -import net.minecraftforge.fml.common.event.FMLFingerprintViolationEvent; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLServerStartingEvent; -import net.minecraftforge.fml.common.registry.GameRegistry; - -/** - * Main entry point for the mod, everything runs through this. - * - * @author DShadowWolf <dshadowwolf@gmail.com> - */ - -@Mod(modid = Constants.MODID, - name = Constants.NAME, - version = Constants.VERSION, - acceptedMinecraftVersions = "[1.12,)", - certificateFingerprint = "@FINGERPRINT@") +import java.util.stream.Collectors; +@Mod("orespawn") public class OreSpawn { - - @Instance - public static OreSpawn instance; - - public static final Logger LOGGER = LogManager.getLogger(Constants.MODID); - public static final OS3API API = new OS3APIImpl(); - static final EventHandlers eventHandlers = new EventHandlers(); - public static final FeatureRegistry FEATURES = new FeatureRegistry(); - - static final FlatBedrock flatBedrock = new FlatBedrock(); - - @EventHandler - public void onFingerprintViolation(final FMLFingerprintViolationEvent event) { - LOGGER.warn("Invalid fingerprint detected!"); - } - - @EventHandler - public void preInit(final FMLPreInitializationEvent ev) { - Config.loadConfig(); - - PluginLoader.INSTANCE.load(ev); - - if (Config.getBoolean(Constants.FLAT_BEDROCK)) { - GameRegistry.registerWorldGenerator(flatBedrock, 100); - } - - if (Config.getBoolean(Constants.RETROGEN_KEY) - || Config.getBoolean(Constants.REPLACE_VANILLA_OREGEN) - || Config.getBoolean(Constants.RETRO_BEDROCK)) { - MinecraftForge.EVENT_BUS.register(eventHandlers); - MinecraftForge.ORE_GEN_BUS.register(eventHandlers); - } - } - - @EventHandler - public void init(final FMLInitializationEvent ev) { - PluginLoader.INSTANCE.register(); - - API.loadConfigFiles(); - } - - @EventHandler - public void postInit(final FMLPostInitializationEvent ev) { - Config.saveConfig(); - API.getAllSpawns().entrySet().stream() - .forEach(ent -> { - GameRegistry.registerWorldGenerator(new OreSpawnFeatureGenerator(ent.getValue(), ent.getKey()), 100); - }); - } - - @EventHandler - public void onServerStarting(final FMLServerStartingEvent ev) { - ev.registerServerCommand(new ClearChunkCommand()); - ev.registerServerCommand(new DumpBiomesCommand()); - ev.registerServerCommand(new AddOreCommand()); - ev.registerServerCommand(new WriteConfigsCommand()); - } + // Directly reference a log4j logger. + public static final Logger LOGGER = LogManager.getLogger(); + + public OreSpawn() { + // 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 the doClientStuff method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); + + // Register ourselves for server and other game events we are interested in + MinecraftForge.EVENT_BUS.register(this); + } + + private void setup(final FMLCommonSetupEvent event) { + final Type annotationType = Type.getType(os3plugin.class); + ModList.get().getAllScanData().stream() + .flatMap( scanData -> scanData.getAnnotations() + .stream() + .filter(a -> annotationType.equals(a.getAnnotationType()))) + .map(ModFileScanData.AnnotationData::getClassType) + .map(TypeUtils::getClassFromType) + .filter(Helpers::objectNotNull) + .forEach(PluginLoader::Load); + // load configs + OS3API.loadPresets(); + OS3API.loadReplacements(); + OS3API.loadFeatures(); + OS3API.loadIntegrationWhitelist(); + OS3API.loadSpawns(); + + // register world gen + } + + private void doClientStuff(final FMLClientSetupEvent event) { + // do something that can only be done on the client + LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings); + } + + private void enqueueIMC(final InterModEnqueueEvent event) { + // some example code to dispatch IMC to another mod + InterModComms.sendTo("orespawn", "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.getMessageSupplier().get()). + collect(Collectors.toList())); + } + + // You can use SubscribeEvent and let the Event Bus discover methods to call + @SubscribeEvent + public void onServerStarting(final 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/java/com/mcmoddev/orespawn/api/BiomeLocation.java b/src/main/java/com/mcmoddev/orespawn/api/BiomeLocation.java deleted file mode 100644 index 6870c78..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/BiomeLocation.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonElement; -import com.mcmoddev.orespawn.util.Collectors2; - -import net.minecraft.world.biome.Biome; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public interface BiomeLocation { - - boolean matches(Biome biome); - - JsonElement serialize(); - - default ImmutableList getBiomes() { - return ForgeRegistries.BIOMES.getValuesCollection().stream().filter(this::matches) - .collect(Collectors2.toImmutableList()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/FeatureBase.java b/src/main/java/com/mcmoddev/orespawn/api/FeatureBase.java deleted file mode 100644 index 5b067d6..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/FeatureBase.java +++ /dev/null @@ -1,314 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.api.os3.OreSpawnBlockMatcher; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; -import net.minecraftforge.registries.IForgeRegistryEntry; - -public class FeatureBase extends IForgeRegistryEntry.Impl { - - private static final int MAX_CACHE_SIZE = 2048; - /** - * overflow cache so that ores that spawn at edge of chunk can appear in the neighboring chunk - * without triggering a chunk-load. - */ - private static final Map> overflowCache = new HashMap<>( - MAX_CACHE_SIZE); - private static final Deque cacheOrder = new LinkedList<>(); - protected Random random; - - public FeatureBase(final Random rand) { - this.random = rand; - } - - public boolean isValidBlock(final IBlockState oreBlock) { - return oreBlock.getBlock() != Blocks.AIR; - } - - protected void runCache(final int chunkX, final int chunkZ, final World world, - final ISpawnEntry spawnData) { - final Vec3i chunkCoord = new Vec3i(chunkX, chunkZ, world.provider.getDimension()); - final Map cache = retrieveCache(chunkCoord); - - if (!cache.isEmpty()) { // if there is something in the cache, try to spawn it - for (final Entry ent : cache.entrySet()) { - spawnNoCheck(cache.get(ent.getKey()), world, ent.getKey(), - world.provider.getDimension(), spawnData); - } - } - } - - protected boolean spawn(final IBlockState oreBlock, final World world, final BlockPos coord, - final int dimension, final boolean cacheOverflow, final ISpawnEntry spawnData) { - if (oreBlock == null) { - OreSpawn.LOGGER.fatal("FeatureBase.spawn() called with a null ore!"); - return false; - } - - if (!isValidBlock(oreBlock)) { - return false; - } - - final Biome thisBiome = world.getBiome(coord); - - if (!spawnData.biomeAllowed(thisBiome.getRegistryName())) { - return false; - } - - final BlockPos np = mungeFixYcoord(coord); - - if (coord.getY() >= world.getHeight()) { - OreSpawn.LOGGER.warn("Asked to spawn {} above build limit at {}", oreBlock, coord); - return false; - } - - return spawnOrCache(world, np, spawnData.getMatcher(), oreBlock, cacheOverflow, dimension, - spawnData); - } - - private BlockPos mungeFixYcoord(final BlockPos coord) { - if (coord.getY() < 0) { - final int newYCoord = coord.getY() * -1; - return new BlockPos(coord.getX(), newYCoord, coord.getZ()); - } else { - return new BlockPos(coord); - } - } - - private boolean spawnOrCache(final World world, final BlockPos coord, - final OreSpawnBlockMatcher replacer, final IBlockState oreBlock, - final boolean cacheOverflow, final int dimension, final ISpawnEntry spawnData) { - if (world.isBlockLoaded(coord)) { - boolean x_bad = false; - boolean z_bad = false; - ChunkPos start = world.getChunk(coord).getPos(); - ChunkPos end = new ChunkPos(start.x+1, start.z+1); - if (coord.getX() < start.getXStart() || coord.getX() > end.getXEnd()) x_bad = true; - if (coord.getZ() < start.getZStart() || coord.getZ() > end.getZEnd()) z_bad = true; - - if(x_bad || z_bad) { - if(cacheOverflow) { - cacheOverflowBlock(oreBlock, coord, dimension); - return true; - } else { - return false; - } - } - if (!isValidBlock(oreBlock)) { - return false; - } - - final IBlockState targetBlock = world.getBlockState(coord); - final Biome thisBiome = world.getBiome(coord); - - if (replacer.test(targetBlock) && spawnData.biomeAllowed(thisBiome.getRegistryName())) { - // don't send block update, send to client, don't re-render, observers don't see the change - world.setBlockState(coord, oreBlock, 0x16); - return true; - } else { - return false; - } - } else if (cacheOverflow) { - cacheOverflowBlock(oreBlock, coord, dimension); - return true; - } - - return false; - } - - private void spawnNoCheck(final IBlockState oreBlock, final World world, final BlockPos coord, - final int dimension, final ISpawnEntry spawnData) { - if (oreBlock == null) { - OreSpawn.LOGGER.fatal("FeatureBase.spawn() called with a null ore!"); - return; - } - - final BlockPos np = mungeFixYcoord(coord); - - if (coord.getY() >= world.getHeight()) { - OreSpawn.LOGGER.warn("Asked to spawn {} above build limit at {}", oreBlock, coord); - return; - } - - spawnOrCache(world, np, spawnData.getMatcher(), oreBlock, false, dimension, spawnData); - } - - private void cacheOverflowBlock(final IBlockState bs, final BlockPos coord, - final int dimension) { - final Vec3i chunkCoord = new Vec3i(coord.getX() / 16, coord.getY() / 16, dimension); - - if (overflowCache.containsKey(chunkCoord)) { - cacheOrder.addLast(chunkCoord); - - if (cacheOrder.size() > MAX_CACHE_SIZE) { - final Vec3i drop = cacheOrder.removeFirst(); - overflowCache.get(drop).clear(); - overflowCache.remove(drop); - } - - overflowCache.put(chunkCoord, new HashMap<>()); - } - - final Map cache = overflowCache.getOrDefault(chunkCoord, - new HashMap<>()); - cache.put(coord, bs); - } - - private Map retrieveCache(final Vec3i chunkCoord) { - if (overflowCache.containsKey(chunkCoord)) { - final Map cache = overflowCache.get(chunkCoord); - cacheOrder.remove(chunkCoord); - overflowCache.remove(chunkCoord); - return cache; - } else { - return Collections.emptyMap(); - } - } - - protected void scramble(final int[] target, final Random prng) { - for (int i = target.length - 1; i > 0; i--) { - final int n = prng.nextInt(i); - final int temp = target[i]; - target[i] = target[n]; - target[n] = temp; - } - } - - protected static final Vec3i[] offsets_small = { - new Vec3i(0, 0, 0), new Vec3i(1, 0, 0), new Vec3i(0, 1, 0), new Vec3i(1, 1, 0), - - new Vec3i(0, 0, 1), new Vec3i(1, 0, 1), new Vec3i(0, 1, 1), new Vec3i(1, 1, 1) - }; - - protected static final Vec3i[] offsets = { - new Vec3i(-1, -1, -1), new Vec3i(0, -1, -1), new Vec3i(1, -1, -1), new Vec3i(-1, 0, -1), - new Vec3i(0, 0, -1), new Vec3i(1, 0, -1), new Vec3i(-1, 1, -1), new Vec3i(0, 1, -1), - new Vec3i(1, 1, -1), - - new Vec3i(-1, -1, 0), new Vec3i(0, -1, 0), new Vec3i(1, -1, 0), new Vec3i(-1, 0, 0), - new Vec3i(0, 0, 0), new Vec3i(1, 0, 0), new Vec3i(-1, 1, 0), new Vec3i(0, 1, 0), - new Vec3i(1, 1, 0), - - new Vec3i(-1, -1, 1), new Vec3i(0, -1, 1), new Vec3i(1, -1, 1), new Vec3i(-1, 0, 1), - new Vec3i(0, 0, 1), new Vec3i(1, 0, 1), new Vec3i(-1, 1, 1), new Vec3i(0, 1, 1), - new Vec3i(1, 1, 1) - }; - - protected static final int[] offsetIndexRef = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26 - }; - protected static final int[] offsetIndexRef_small = { - 0, 1, 2, 3, 4, 5, 6, 7 - }; - - protected static void mergeDefaults(final JsonObject parameters, - final JsonObject defaultParameters) { - defaultParameters.entrySet().forEach(entry -> { - if (!parameters.has(entry.getKey())) { - parameters.add(entry.getKey(), entry.getValue()); - } - }); - } - - private double triangularDistribution(final double a, final double b, final double c) { - final double base = (c - a) / (b - a); - final double rand = this.random.nextDouble(); - - if (rand < base) { - return a + Math.sqrt(rand * (b - a) * (c - a)); - } else { - return b - Math.sqrt((1 - rand) * (b - a) * (b - c)); - } - } - - protected int getPoint(final int lowerBound, final int upperBound, final int median) { - final int t = (int) Math.round( - triangularDistribution((float) lowerBound, (float) upperBound, (float) median)); - return t - median; - } - - protected void spawnMungeInner(final Random prng, final int rSqr, int quantity, - final Vec3i vals, final ISpawnEntry spawnData, final World world, final BlockPos blockPos) { - int dx = vals.getX(); - int dy = vals.getY(); - int dz = vals.getZ(); - final IBlockList possibleOres = spawnData.getBlocks(); - final OreSpawnBlockMatcher replacer = spawnData.getMatcher(); - - if ((dx * dx + dy * dy + dz * dz) <= rSqr) { - final IBlockState oreBlock = possibleOres.getRandomBlock(prng); - if (oreBlock.getBlock().equals(net.minecraft.init.Blocks.AIR)) return; - spawnOrCache(world, blockPos.add(dx, dy, dz), replacer, oreBlock, true, - world.provider.getDimension(), spawnData); - quantity--; - } - } - - protected void spawnMungeSW(final World world, final BlockPos blockPos, final int rSqr, - final double radius, final ISpawnEntry spawnData, final int count) { - final Random prng = this.random; - int quantity = count; - for (int dy = (int) (-1 * radius); dy < radius; dy++) { - for (int dx = (int) (radius); dx >= (int) (-1 * radius); dx--) { - for (int dz = (int) (radius); dz >= (int) (-1 * radius); dz--) { - spawnMungeInner(prng, rSqr, quantity, new Vec3i(dx,dy,dz), spawnData, world, blockPos); - if (quantity <= 0) { - return; - } - } - } - } - } - - protected void spawnMungeNE(final World world, final BlockPos blockPos, final int rSqr, - final double radius, final ISpawnEntry spawnData, final int count) { - final Random prng = this.random; - int quantity = count; -// final IBlockList possibleOres = spawnData.getBlocks(); -// final OreSpawnBlockMatcher replacer = spawnData.getMatcher(); - for (int dy = (int) (-1 * radius); dy < radius; dy++) { - for (int dz = (int) (-1 * radius); dz < radius; dz++) { - for (int dx = (int) (-1 * radius); dx < radius; dx++) { - spawnMungeInner(prng, rSqr, quantity, new Vec3i(dx,dy,dz), spawnData, world, blockPos); - if (quantity <= 0) { - return; - } - } - } - } - } - - protected int getABC(final int dx, final int dy, final int dz) { - return (dx * dx + dy * dy + dz * dz); - } - - protected int countItem(final int dx, final boolean toPositive) { - return toPositive ? dx + 1 : dx - 1; - } - - protected boolean endCheck(final boolean toPositive, final int dx, final double radius) { - return toPositive ? (dx >= getStart(toPositive, radius)) : (dx < radius); - } - - protected int getStart(final boolean toPositive, final double radius) { - return ((int) (radius * (toPositive ? 1 : -1))); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/IBlockList.java b/src/main/java/com/mcmoddev/orespawn/api/IBlockList.java deleted file mode 100644 index 26e12bb..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/IBlockList.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import java.util.Random; - -import com.mcmoddev.orespawn.api.os3.IBlockDefinition; - -import net.minecraft.block.state.IBlockState; - -public interface IBlockList { - - void addBlock(IBlockDefinition block); - - IBlockState getRandomBlock(Random rand); - - void startNewSpawn(); - - void dump(); - - int count(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/IDimensionList.java b/src/main/java/com/mcmoddev/orespawn/api/IDimensionList.java deleted file mode 100644 index 1156481..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/IDimensionList.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import com.google.gson.JsonObject; - -public interface IDimensionList { - - JsonObject serialize(); - - default boolean matches(int dimensionId) { - return false; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/IFeature.java b/src/main/java/com/mcmoddev/orespawn/api/IFeature.java deleted file mode 100644 index 82f04a6..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/IFeature.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; - -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.registries.IForgeRegistryEntry; - -public interface IFeature extends IForgeRegistryEntry { - - void generate(World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider, - ISpawnEntry spawn, ChunkPos pos); - - void setRandom(Random rand); - - JsonObject getDefaultParameters(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS3API.java b/src/main/java/com/mcmoddev/orespawn/api/OS3API.java new file mode 100644 index 0000000..f7b9c9d --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/api/OS3API.java @@ -0,0 +1,177 @@ +package com.mcmoddev.orespawn.api; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.commons.io.FileUtils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.Constants; +import com.mcmoddev.orespawn.data.OS4BlockData; +import com.mcmoddev.orespawn.registries.FeaturesRegistry; +import com.mcmoddev.orespawn.registries.PresetsRegistry; +import com.mcmoddev.orespawn.registries.ReplacementsRegistry; + +public class OS3API { + private static Map modRefs = new HashMap<>(1024); + private static List allowedConfigs = new LinkedList<>(); + + public static void addMod(final String modId, final String modPath) { + modRefs.put(modId, modPath); + } + + public static Map getMods() { + return Collections.unmodifiableMap(modRefs); + } + + public static void loadPresets() { + PathMatcher matcher = FileSystems.getDefault() + .getPathMatcher("glob:**/presets-*.json"); + + // find all `presets_XXX.json` files + try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { + stream.filter(matcher::matches).map(Path::toFile) + // load them into a registry + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonObject elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( entry -> { + String entryZone = entry.getKey(); + entry.getValue().getAsJsonObject().entrySet().stream() + .forEach( subEnt -> { + String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); + PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); + }); + + }); + }); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + } + } + + public static void loadReplacements() { + PathMatcher matcher = FileSystems.getDefault() + .getPathMatcher("glob:**/replacements-*.json"); + + try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { + stream.filter(matcher::matches).map(Path::toFile) + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonObject elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( repl -> { + String replName = repl.getKey(); + if (repl.getValue().isJsonPrimitive()) { + // TODO: Preset Shit + } else { + JsonArray replValues = repl.getValue().getAsJsonArray(); + List blocks = new LinkedList(); + for( JsonElement el : replValues) { + JsonObject zz = el.getAsJsonObject(); + String blName = zz.get("name").getAsString(); + String state = zz.has("state")?zz.get("state").getAsString():""; + blocks.add(new OS4BlockData(blName, state)); + } + ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); + } + }); + }); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + } + } + + public static void loadFeatures() { + PathMatcher matcher = FileSystems.getDefault() + .getPathMatcher("glob:**/features-*.json"); + + try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { + stream.filter(matcher::matches).map(Path::toFile) + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonArray elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonArray(); + + elements.forEach( ent -> { + JsonObject entry = ent.getAsJsonObject(); + String featureName = entry.get("name").getAsString(); + String className = entry.get("class").getAsString(); + FeaturesRegistry.INSTANCE.addFeature(featureName, className); + }); + }); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + } + } + + public static void loadIntegrationWhitelist() { + Path p = Constants.SYSCONF.resolve(Constants.FileBits.ALLOWED_MODS); + final JsonParser parser = new JsonParser(); + String rawJson; + + try { + rawJson = FileUtils.readFileToString(p.toFile(), Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + parser.parse(rawJson).getAsJsonArray().forEach( item -> allowedConfigs.add(item.getAsString())); + } + + public static void loadSpawns() { + // find all JSON files that are not in `sysconf` + // filter for only active-flagged files + // load them, interpolating any presets present + // stuff them into a registry + // return + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java new file mode 100644 index 0000000..29ee672 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java @@ -0,0 +1,5 @@ +package com.mcmoddev.orespawn.api; + +public class OS4Feature { + +} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadStateValueException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadStateValueException.java deleted file mode 100755 index 0192989..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadStateValueException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -public class BadStateValueException extends Exception { - - /** - * - */ - private static final long serialVersionUID = 3826628238012469423L; - - public BadStateValueException(String msg) { - super(msg); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadValueException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadValueException.java deleted file mode 100644 index a71123f..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/BadValueException.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -import java.util.Locale; - -public class BadValueException extends Exception { - - private static final long serialVersionUID = 1143938140559149506L; - private final String keyName; - private final String keyValue; - - public BadValueException(final String keyName, final String keyValue) { - super(); - this.keyName = keyName; - this.keyValue = keyValue; - } - - @Override - public String getMessage() { - final String baseMessage = super.getMessage(); - return String.format(Locale.ENGLISH, "Unknown value %s for key %s%n%s", this.keyValue, this.keyName, - baseMessage); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/MissingVersionException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/MissingVersionException.java deleted file mode 100644 index 8c98fff..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/MissingVersionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -public class MissingVersionException extends Exception { - - /** - * - */ - private static final long serialVersionUID = -4306852267351590384L; - -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/NotAProperConfigException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/NotAProperConfigException.java deleted file mode 100644 index 2f31a43..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/NotAProperConfigException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -public class NotAProperConfigException extends Exception { - - /** - * - */ - private static final long serialVersionUID = -7748241590958198482L; - -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/OldVersionException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/OldVersionException.java deleted file mode 100644 index 62b5fa7..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/OldVersionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -public class OldVersionException extends Exception { - - /** - * - */ - private static final long serialVersionUID = 3760017140789193369L; - -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownFieldException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownFieldException.java deleted file mode 100644 index e6008d1..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownFieldException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -import java.util.Locale; - -public class UnknownFieldException extends Exception { - - private static final long serialVersionUID = 1L; - private final String message; - - public UnknownFieldException(final String theField) { - super(); - this.message = String.format(Locale.ENGLISH, "Unkown field %s in config", theField); - } - - @Override - public String getMessage() { - final String baseMessage = super.getMessage(); - return String.format(Locale.ENGLISH, "%s%n%s", this.message, baseMessage); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownNameException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownNameException.java deleted file mode 100644 index 94bf09a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownNameException.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -import java.util.Locale; - -public class UnknownNameException extends Exception { - - private static final long serialVersionUID = -3426121906665390773L; - private final String fieldName; - private final String fieldValue; - - public UnknownNameException(final String fieldName, final String fieldValue) { - super(); - this.fieldName = fieldName; - this.fieldValue = fieldValue; - } - - @Override - public String getMessage() { - final String baseMessage = super.getMessage(); - return String.format(Locale.ENGLISH, "Unknown %s name %s%n%s", this.fieldName, this.fieldValue, - baseMessage); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownVersionException.java b/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownVersionException.java deleted file mode 100644 index fa369ca..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/exceptions/UnknownVersionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mcmoddev.orespawn.api.exceptions; - -public class UnknownVersionException extends Exception { - - /** - * - */ - private static final long serialVersionUID = 3817238409227005355L; - -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IBiomeBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IBiomeBuilder.java deleted file mode 100644 index 1bda5db..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IBiomeBuilder.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import com.mcmoddev.orespawn.api.BiomeLocation; - -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.biome.Biome; - -public interface IBiomeBuilder { - - IBiomeBuilder addWhitelistEntry(Biome biome); - - IBiomeBuilder addWhitelistEntry(String biomeName); - - IBiomeBuilder addWhitelistEntry(ResourceLocation biomeResourceLocation); - - IBiomeBuilder addBlacklistEntry(Biome biome); - - IBiomeBuilder addBlacklistEntry(String biomeName); - - IBiomeBuilder addBlacklistEntry(ResourceLocation biomeResourceLocation); - - IBiomeBuilder setAcceptAll(); - - BiomeLocation create(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockBuilder.java deleted file mode 100644 index 54baec3..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockBuilder.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; - -public interface IBlockBuilder { - - /** - * - * @param blockState - * @return - */ - IBlockBuilder setFromBlockState(IBlockState blockState); - - /** - * - * @param block - * @return - */ - IBlockBuilder setFromBlock(Block block); - - /** - * - * @param blockName - * @return - */ - IBlockBuilder setFromName(String blockName); - - /** - * - * @param blockName - * @param state - * @return - */ - IBlockBuilder setFromName(String blockName, String state); - - /** - * - * @param blockName - * @param metadata - * @return - * @deprecated - */ - @Deprecated - IBlockBuilder setFromName(String blockName, int metadata); - - /** - * - * @param blockResourceLocation - * @return - */ - IBlockBuilder setFromName(ResourceLocation blockResourceLocation); - - /** - * - * @param blockResourceLocation - * @param state - * @return - */ - IBlockBuilder setFromName(ResourceLocation blockResourceLocation, String state); - - /** - * - * @param blockResourceLocation - * @param metadata - * @return - * @deprecated - */ - @Deprecated - IBlockBuilder setFromName(ResourceLocation blockResourceLocation, int metadata); - - /** - * - * @param blockState - * @param chance - * @return - */ - IBlockBuilder setFromBlockStateWithChance(IBlockState blockState, int chance); - - /** - * - * @param block - * @param chance - * @return - */ - IBlockBuilder setFromBlockWithChance(Block block, int chance); - - /** - * - * @param blockName - * @param chance - * @return - */ - IBlockBuilder setFromNameWithChance(String blockName, int chance); - - /** - * - * @param blockName - * @param state - * @param chance - * @return - */ - IBlockBuilder setFromNameWithChance(String blockName, String state, int chance); - - /** - * - * @param blockName - * @param metadata - * @param chance - * @return - * @deprecated - */ - @Deprecated - IBlockBuilder setFromNameWithChance(String blockName, int metadata, int chance); - - /** - * - * @param blockResourceLocation - * @param chance - * @return - */ - IBlockBuilder setFromNameWithChance(ResourceLocation blockResourceLocation, int chance); - - /** - * - * @param blockResourceLocation - * @param state - * @param chance - * @return - */ - IBlockBuilder setFromNameWithChance(ResourceLocation blockResourceLocation, String state, - int chance); - - /** - * - * @param blockResourceLocation - * @param metadata - * @param chance - * @return - * @deprecated - */ - @Deprecated - IBlockBuilder setFromNameWithChance(ResourceLocation blockResourceLocation, int metadata, - int chance); - - /** - * - * @param chance - * @return - */ - IBlockBuilder setChance(int chance); - - /** - * - * @return - */ - IBlockDefinition create(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockDefinition.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockDefinition.java deleted file mode 100644 index 03bec55..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IBlockDefinition.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import net.minecraft.block.state.IBlockState; - -public interface IBlockDefinition { - - IBlockState getBlock(); - - int getChance(); - - default boolean isValid() { - return true; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IDimensionBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IDimensionBuilder.java deleted file mode 100644 index 1fe97bb..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IDimensionBuilder.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import com.mcmoddev.orespawn.api.IDimensionList; - -public interface IDimensionBuilder { - - IDimensionBuilder addWhitelistEntry(int dimensionID); - - IDimensionBuilder addBlacklistEntry(int dimensionID); - - IDimensionBuilder setAcceptAll(); - - IDimensionBuilder setDenyAll(); - - IDimensionBuilder setAcceptAllOverworld(); - - IDimensionList create(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureBuilder.java deleted file mode 100644 index 20fefa2..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureBuilder.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import com.google.gson.JsonElement; -import com.mcmoddev.orespawn.api.IFeature; - -import net.minecraft.util.ResourceLocation; - -public interface IFeatureBuilder { - - IFeatureBuilder setFeature(String featureName); - - IFeatureBuilder setFeature(ResourceLocation featureResourceLocation); - - IFeatureBuilder setFeature(IFeature feature); - - IFeatureBuilder setParameter(String parameterName, String parameterValue); - - IFeatureBuilder setParameter(String parameterName, int parameterValue); - - IFeatureBuilder setParameter(String parameterName, float parameterValue); - - IFeatureBuilder setParameter(String parameterName, boolean parameterValue); - - IFeatureBuilder setParameter(String parameterName, JsonElement parameterValue); - - IFeatureBuilder setUseFeatureDefaults(); - - IFeatureEntry create(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureEntry.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureEntry.java deleted file mode 100644 index 6f5fb06..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IFeatureEntry.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.IFeature; - -public interface IFeatureEntry { - - IFeature getFeature(); - - String getFeatureName(); - - JsonObject getFeatureParameters(); - - void setParameter(String parameterName, String parameterValue); - - void setParameter(String parameterName, int parameterValue); - - void setParameter(String parameterName, boolean parameterValue); - - void setParameter(String parameterName, float parameterValue); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementBuilder.java deleted file mode 100644 index ecf91e7..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementBuilder.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; - -public interface IReplacementBuilder { - - /** - * - * @param entryName - * @return - */ - IReplacementBuilder setFromName(String entryName); - - /** - * - * @param name - * @return - */ - IReplacementBuilder setName(String name); - - /** - * - * @param blockState - * @return - */ - IReplacementBuilder addEntry(IBlockState blockState); - - /** - * - * @param blockName - * @return - */ - IReplacementBuilder addEntry(String blockName); - - /** - * - * @param blockName - * @param state - * @return - */ - IReplacementBuilder addEntry(String blockName, String state); - - /** - * - * @param blockName - * @param metadata - * @return - * @deprecated - */ - @Deprecated - IReplacementBuilder addEntry(String blockName, int metadata); - - /** - * - * @param blockResourceLocation - * @return - */ - IReplacementBuilder addEntry(ResourceLocation blockResourceLocation); - - /** - * - * @param blockResourceLocation - * @param state - * @return - */ - IReplacementBuilder addEntry(ResourceLocation blockResourceLocation, String state); - - /** - * - * @param blockResourceLocation - * @param metadata - * @return - * @deprecated - */ - @Deprecated - IReplacementBuilder addEntry(ResourceLocation blockResourceLocation, int metadata); - - boolean hasEntries(); - - IReplacementEntry create(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementEntry.java b/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementEntry.java deleted file mode 100644 index e9f03f9..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/IReplacementEntry.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import java.util.List; - -import net.minecraft.block.state.IBlockState; -import net.minecraftforge.registries.IForgeRegistryEntry; - -public interface IReplacementEntry extends IForgeRegistryEntry { - - OreSpawnBlockMatcher getMatcher(); - - List getEntries(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnBuilder.java deleted file mode 100644 index 3808d61..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnBuilder.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.api.IDimensionList; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; - -public interface ISpawnBuilder { - - ISpawnBuilder setName(String name); - - ISpawnBuilder setDimensions(IDimensionList dimensions); - - ISpawnBuilder setBiomes(BiomeLocation biomes); - - ISpawnBuilder setEnabled(boolean enabled); - - ISpawnBuilder setRetrogen(boolean retrogen); - - ISpawnBuilder setReplacement(IReplacementEntry replacements); - - ISpawnBuilder setFeature(IFeatureEntry feature); - - ISpawnBuilder addBlock(String blockName); - - ISpawnBuilder addBlock(String blockName, String blockState); - - ISpawnBuilder addBlock(String blockName, int blockMetadata); - - ISpawnBuilder addBlock(ResourceLocation blockResourceLocation); - - ISpawnBuilder addBlock(ResourceLocation blockResourceLocation, String blockState); - - ISpawnBuilder addBlock(ResourceLocation blockResourceLocation, int blockMetadata); - - ISpawnBuilder addBlock(Block block); - - ISpawnBuilder addBlock(IBlockState block); - - ISpawnBuilder addBlockWithChance(String blockName, int chance); - - ISpawnBuilder addBlockWithChance(String blockName, String blockState, int chance); - - ISpawnBuilder addBlockWithChance(String blockName, int blockMetadata, int chance); - - ISpawnBuilder addBlockWithChance(ResourceLocation blockResourceLocation, int chance); - - ISpawnBuilder addBlockWithChance(ResourceLocation blockResourceLocation, String blockState, - int chance); - - ISpawnBuilder addBlockWithChance(ResourceLocation blockResourceLocation, int blockMetadata, - int chance); - - ISpawnBuilder addBlockWithChance(Block block, int chance); - - ISpawnBuilder addBlockWithChance(IBlockState block, int chance); - - ISpawnEntry create(); - - ISpawnBuilder addBlock(IBlockDefinition block); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnEntry.java b/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnEntry.java deleted file mode 100644 index 7fdaceb..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/ISpawnEntry.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import java.util.Random; - -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.api.IBlockList; -import com.mcmoddev.orespawn.api.IDimensionList; - -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public interface ISpawnEntry { - - default boolean isEnabled() { - return false; - } - - default boolean isRetrogen() { - return false; - } - - String getSpawnName(); - - boolean dimensionAllowed(int dimension); - - boolean biomeAllowed(ResourceLocation biomeName); - - boolean biomeAllowed(Biome biome); - - IFeatureEntry getFeature(); - - OreSpawnBlockMatcher getMatcher(); - - IBlockList getBlocks(); - - void generate(Random random, World world, IChunkGenerator chunkGenerator, - IChunkProvider chunkProvider, ChunkPos pos); - - IDimensionList getDimensions(); - - BiomeLocation getBiomes(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/OS3API.java b/src/main/java/com/mcmoddev/orespawn/api/os3/OS3API.java deleted file mode 100644 index cc421f8..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/OS3API.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; - -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.data.PresetsStorage; -import com.mcmoddev.orespawn.worldgen.OreSpawnFeatureGenerator; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; - -public interface OS3API { - - void addSpawn(ISpawnEntry spawnEntry); - - void addFeature(String featureName, IFeature feature); - - void addReplacement(IReplacementEntry replacementEntry); - - Map getReplacements(); - - IReplacementEntry getReplacement(String replacementName); - - List getSpawns(int dimensionID); - - ISpawnEntry getSpawn(String spawnName); - - Map getAllSpawns(); - - List getDimensionDefaultReplacements(int dimensionID); - - ISpawnBuilder getSpawnBuilder(); - - IDimensionBuilder getDimensionBuilder(); - - IFeatureBuilder getFeatureBuilder(); - - IBlockBuilder getBlockBuilder(); - - IBiomeBuilder getBiomeBuilder(); - - IReplacementBuilder getReplacementBuilder(); - - boolean featureExists(String featureName); - - boolean featureExists(ResourceLocation featureName); - - IFeature getFeature(String featureName); - - IFeature getFeature(ResourceLocation featureName); - - PresetsStorage copyPresets(); - - void loadConfigFiles(); - - boolean hasReplacement(ResourceLocation resourceLocation); - - boolean hasReplacement(String name); - - void mapEntryToFile(Path p, String entryName); - - List getSpawnsForFile(String fileName); - - Map> getSpawnsByFile(); - - void addGenerator(final OreSpawnFeatureGenerator generator); - - List getGenerators(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/OS3FeatureGenerator.java b/src/main/java/com/mcmoddev/orespawn/api/os3/OS3FeatureGenerator.java deleted file mode 100644 index d803f9d..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/OS3FeatureGenerator.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -public interface OS3FeatureGenerator { - public ISpawnEntry getSpawnData(); - public String getSpawnName(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/OreSpawnBlockMatcher.java b/src/main/java/com/mcmoddev/orespawn/api/os3/OreSpawnBlockMatcher.java deleted file mode 100644 index 0c71641..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/OreSpawnBlockMatcher.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.mcmoddev.orespawn.api.os3; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Predicate; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; - -public class OreSpawnBlockMatcher implements Predicate { - - private final List possibles; - - public OreSpawnBlockMatcher(final IBlockState... matches) { - this.possibles = Arrays.asList(matches); - } - - public OreSpawnBlockMatcher(final List matches) { - this.possibles = new ArrayList<>(); - this.possibles.addAll(matches); - } - - private boolean has(final IBlockState bs) { - return this.possibles.stream().filter(bs::equals).count() > 0; - } - - public boolean test(final IBlockState other) { - return (!other.getBlock().equals(Blocks.AIR)) && this.has(other); - } - - public JsonArray serialize() { - final JsonArray rv = new JsonArray(); - - possibles.stream().forEach(bs -> { - final JsonObject t = new JsonObject(); - t.addProperty(Constants.ConfigNames.NAME, bs.getBlock().getRegistryName().toString()); - if (!bs.equals(bs.getBlock().getDefaultState())) { - final String state = StateUtil.serializeState(bs); - t.addProperty(Constants.ConfigNames.STATE, state); - } - rv.add(t); - }); - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3/package-info.java b/src/main/java/com/mcmoddev/orespawn/api/os3/package-info.java deleted file mode 100644 index 3db5c1b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/os3/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -@javax.annotation.ParametersAreNonnullByDefault -package com.mcmoddev.orespawn.api.os3; diff --git a/src/main/java/com/mcmoddev/orespawn/api/plugin/OreSpawnPlugin.java b/src/main/java/com/mcmoddev/orespawn/api/os3plugin.java similarity index 87% rename from src/main/java/com/mcmoddev/orespawn/api/plugin/OreSpawnPlugin.java rename to src/main/java/com/mcmoddev/orespawn/api/os3plugin.java index 7120c8b..c0be996 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/plugin/OreSpawnPlugin.java +++ b/src/main/java/com/mcmoddev/orespawn/api/os3plugin.java @@ -1,4 +1,4 @@ -package com.mcmoddev.orespawn.api.plugin; +package com.mcmoddev.orespawn.api; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -8,7 +8,7 @@ @Retention(RUNTIME) @Target(TYPE) -public @interface OreSpawnPlugin { +public @interface os3plugin { // the Mod this is for - will be used for // generating the name of the json the config diff --git a/src/main/java/com/mcmoddev/orespawn/api/plugin/IOreSpawnPlugin.java b/src/main/java/com/mcmoddev/orespawn/api/plugin/IOreSpawnPlugin.java deleted file mode 100644 index d3b0bf7..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/plugin/IOreSpawnPlugin.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mcmoddev.orespawn.api.plugin; - -import com.mcmoddev.orespawn.api.os3.OS3API; - -public interface IOreSpawnPlugin { - - void register(OS3API apiInterface); -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java b/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java index 7af04f4..b6af922 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java +++ b/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java @@ -10,10 +10,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Locale; import java.util.stream.Stream; @@ -22,111 +20,57 @@ import org.apache.commons.io.IOUtils; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS3API; +import com.mcmoddev.orespawn.api.os3plugin; import com.mcmoddev.orespawn.data.Config; import com.mcmoddev.orespawn.data.Constants; -import net.minecraft.crash.CrashReport; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; - public enum PluginLoader { - INSTANCE; - - private class PluginData { - - public final String modId; - public final String resourcePath; - public final IOreSpawnPlugin plugin; - - PluginData(final String modId, final String resourcePath, final IOreSpawnPlugin plugin) { - this.modId = modId; - this.resourcePath = resourcePath; - this.plugin = plugin; - } - } - - private static List dataStore = new ArrayList<>(); - - private String getAnnotationItem(final String item, final ASMData asmData) { - if (asmData.getAnnotationInfo().get(item) != null) { - return asmData.getAnnotationInfo().get(item).toString(); - } else { - return ""; - } - } - - public void load(final FMLPreInitializationEvent event) { - for (final ASMData asmDataItem : event.getAsmData() - .getAll(OreSpawnPlugin.class.getCanonicalName())) { - final String modId = getAnnotationItem("modid", asmDataItem); - final String resourceBase = getAnnotationItem("resourcePath", asmDataItem); - final String clazz = asmDataItem.getClassName(); - IOreSpawnPlugin integration; - - try { - integration = Class.forName(clazz).asSubclass(IOreSpawnPlugin.class).newInstance(); - final PluginData pd = new PluginData(modId, resourceBase, integration); - OreSpawn.LOGGER.fatal("Loading Integration For {}", modId); - dataStore.add(pd); - } catch (final Exception ex) { - OreSpawn.LOGGER.error("Couldn't load integrations for " + modId, ex); - } + + public static void Load(Class clazz) { + if( clazz.isAnnotationPresent(os3plugin.class) ) { + loadInternal(clazz); } } - - public void register() { - dataStore.forEach(pd -> { - scanResources(pd); - pd.plugin.register(OreSpawn.API); - }); + + private static void loadInternal(Class cls) { + final os3plugin annot = cls.getAnnotation(os3plugin.class); + OS3API.addMod(annot.modid(), annot.resourcePath()); } - public void scanResources(final PluginData pd) { - if (Config.getKnownMods().contains(pd.modId)) { - return; - } - - final String base = String.format(Locale.ENGLISH, "assets/%s/%s", pd.modId, pd.resourcePath); - final URL resURL = getClass().getClassLoader().getResource(base); - - if (resURL == null) { - OreSpawn.LOGGER.warn("Unable to access file {}: got 'null' when trying to resolve it", - base); - return; - } - - URI uri; + public static void findResources() { + OS3API.getMods().entrySet() + .forEach( ent -> { + final String base = String.format(Locale.ENGLISH, "assets/%s/%s", ent.getKey(), ent.getValue()); + final URL resURL = INSTANCE.getClass().getClassLoader().getResource(base); + + URI uri; + + try { + uri = resURL.toURI(); + } catch (URISyntaxException ex) { + OreSpawn.LOGGER.error(ex.getMessage()); + return; + } - try { - uri = resURL.toURI(); - } catch (URISyntaxException ex) { - CrashReport report = CrashReport.makeCrashReport(ex, - String.format(Locale.ENGLISH, "Failed to get URI for %s", - (new ResourceLocation(pd.modId, pd.resourcePath)).toString())); - report.getCategory().addCrashSection(Constants.CRASH_SECTION, Constants.VERSION); - return; - } + if (uri.getScheme().equals("jar")) { + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, + Collections.emptyMap())) { + copyout(fileSystem.getPath(base), ent.getKey()); + } catch (IOException exc) { + OreSpawn.LOGGER.error(exc.getMessage()); + return; + } + } else { + copyout(Paths.get(uri), ent.getKey()); } - if (uri.getScheme().equals("jar")) { - try (FileSystem fileSystem = FileSystems.newFileSystem(uri, - Collections.emptyMap())) { - copyout(fileSystem.getPath(base), pd.modId); - } catch (IOException exc) { - CrashReport report = CrashReport.makeCrashReport(exc, String.format( - Locale.ENGLISH, "Failed in getting FileSystem handler set up for %s", uri.getPath())); - report.getCategory().addCrashSection(Constants.CRASH_SECTION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } else { - copyout(Paths.get(uri), pd.modId); - } + Config.addKnownMod(ent.getKey()); - Config.addKnownMod(pd.modId); + }); } - - private void copyout(final Path myPath, final String modId) { + + private static void copyout(final Path myPath, final String modId) { try (Stream walk = Files.walk(myPath, 1)) { for (final Iterator it = walk.iterator(); it.hasNext();) { final Path p = it.next(); @@ -137,15 +81,15 @@ private void copyout(final Path myPath, final String modId) { Path target; if ("_features".equals(FilenameUtils.getBaseName(name))) { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS3, + target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, Constants.FileBits.SYSCONF, String.format(Locale.ENGLISH, "features-%s.json", modId)); } else if ("_replacements".equals(FilenameUtils.getBaseName(name))) { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS3, + target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, Constants.FileBits.SYSCONF, String.format(Locale.ENGLISH, "replacements-%s.json", modId)); } else { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS3, + target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, String.format(Locale.ENGLISH, "%s.json", modId)); } @@ -157,10 +101,8 @@ private void copyout(final Path myPath, final String modId) { } } } catch (IOException exc) { - CrashReport report = CrashReport.makeCrashReport(exc, String.format( - Locale.ENGLISH, "Faulted while iterating %s for config files or copying them out", myPath)); - report.getCategory().addCrashSection(Constants.CRASH_SECTION, Constants.VERSION); - OreSpawn.LOGGER.error(report.getCompleteReport()); + OreSpawn.LOGGER.error(exc.getMessage()); } } + } diff --git a/src/main/java/com/mcmoddev/orespawn/commands/AddOreCommand.java b/src/main/java/com/mcmoddev/orespawn/commands/AddOreCommand.java deleted file mode 100644 index 9219dbc..0000000 --- a/src/main/java/com/mcmoddev/orespawn/commands/AddOreCommand.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.mcmoddev.orespawn.commands; - -import org.apache.commons.io.FilenameUtils; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.Constants.ConfigNames; -import com.mcmoddev.orespawn.json.OreSpawnReader; -import com.mcmoddev.orespawn.json.OreSpawnWriter; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.EnumHand; - -public class AddOreCommand extends CommandBase { - - @Override - public String getName() { - return "addore"; - } - - @Override - public String getUsage(final ICommandSender sender) { - return "/addore "; - } - - @Override - public void execute(final MinecraftServer server, final ICommandSender sender, - final String[] args) throws CommandException { - if (!(sender instanceof EntityPlayer)) { - throw new CommandException("Only players can use this command"); - } - - final EntityPlayer player = (EntityPlayer) sender; - final ItemStack stack = player.getHeldItem(EnumHand.MAIN_HAND); - - if (stack == null) { - throw new CommandException("You have no item in your main hand"); - } else if (!(stack.getItem() instanceof ItemBlock)) { - throw new CommandException("The item in your main hand isn't a block"); - } else if (args.length < 1) { - throw new CommandException(this.getUsage(sender)); - } - - final String file = args[0]; - @SuppressWarnings("deprecation") - final IBlockState state = Block.getBlockFromItem(stack.getItem()) - .getStateFromMeta(stack.getItemDamage()); - - final String rawData = getChatComponentFromNthArg(sender, args, 1).getUnformattedText(); - final JsonParser p = new JsonParser(); - final JsonElement parsed; - if(rawData != null && rawData.length() > 0) { - parsed = mergeDefaults(p.parse(rawData), state); - } else { - parsed = mergeDefaults(p.parse("{}"), state); - } - OreSpawnReader.loadFromJson(FilenameUtils.getBaseName(file), parsed); - OreSpawnWriter.saveSingle(FilenameUtils.getBaseName(file)); - } - - private JsonElement mergeDefaults(final JsonElement parse, final IBlockState state) { - final JsonObject work = parse.getAsJsonObject(); - final JsonObject emptyBlacklist = new JsonObject(); - emptyBlacklist.add("excludes", new JsonArray()); - - if (!work.has(ConfigNames.ENABLED)) { - work.addProperty(ConfigNames.ENABLED, true); - } - if (!work.has(ConfigNames.RETROGEN)) { - work.addProperty(ConfigNames.RETROGEN, false); - } - if (!work.has(ConfigNames.FEATURE)) { - work.addProperty(ConfigNames.FEATURE, "orespawn:default"); - } - if (!work.has(ConfigNames.REPLACEMENT)) { - work.addProperty(ConfigNames.REPLACEMENT, "orespawn:default"); - } - if (!work.has(ConfigNames.PARAMETERS)) { - work.add(ConfigNames.PARAMETERS, - OreSpawn.API.getFeature(work.get(ConfigNames.FEATURE).getAsString()) - .getDefaultParameters()); - } - if (!work.has(ConfigNames.DIMENSIONS)) { - work.add(ConfigNames.DIMENSIONS, emptyBlacklist); - } - if (!work.has(ConfigNames.BIOMES)) { - work.add(ConfigNames.BIOMES, emptyBlacklist); - } - - final JsonObject block = new JsonObject(); - block.addProperty(ConfigNames.CHANCE, 100); - block.addProperty(ConfigNames.NAME, state.getBlock().getRegistryName().toString()); - block.addProperty(ConfigNames.STATE, StateUtil.serializeState(state)); - final JsonArray blocks = new JsonArray(); - blocks.add(block); - work.add(ConfigNames.BLOCK, blocks); - - return work; - } - - @Override - public int compareTo(final ICommand command) { - return this.getName().compareTo(command.getName()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/commands/ClearChunkCommand.java b/src/main/java/com/mcmoddev/orespawn/commands/ClearChunkCommand.java deleted file mode 100644 index f994de9..0000000 --- a/src/main/java/com/mcmoddev/orespawn/commands/ClearChunkCommand.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.mcmoddev.orespawn.commands; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import com.mcmoddev.orespawn.OreSpawn; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.item.ItemStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.world.chunk.Chunk; -import net.minecraftforge.fml.common.registry.ForgeRegistries; -import net.minecraftforge.oredict.OreDictionary; - -public class ClearChunkCommand extends CommandBase { - - private static final String STONE_ID = "minecraft:stone"; - private static final List stoneVariants = Arrays.asList(STONE_ID, "minecraft:diorite", - "minecraft:andesite", "minecraft:granite", "minecraft:sandstone", - "minecraft:red_sandstone", "minecraft:netherrack", "minecraft:end_stone"); - private static final List baseStones = Arrays.asList(STONE_ID, "minecraft:netherrack", - "minecraft:end_stone", "minecraft:cobblestone", "minecraft:obsidian", "minecraft:magma", - "minecraft:soul_sand"); - - private static final List dirtVariants = Arrays.asList("minecraft:dirt", - "minecraft:grass"); - private static final List otherVariants = Arrays.asList("minecraft:gravel", - "minecraft:sand"); - - @Override - public String getName() { - return "clearchunk"; - } - - @Override - public String getUsage(final ICommandSender sender) { - return "/clearchunk "; - } - - @Override - public void execute(final MinecraftServer server, final ICommandSender sender, - final String[] args) throws CommandException { - if (!(sender instanceof EntityPlayer)) { - throw new CommandException("Only players can use this command"); - } - - final EntityPlayer player = (EntityPlayer) sender; - final Chunk chunk = player.getEntityWorld().getChunk(player.getPosition()); - final ChunkPos chunkPos = chunk.getPos(); - final List blocks; - - final boolean flagClassic = args.length > 0 && args[0].toLowerCase().equalsIgnoreCase("classic"); - - final List blockNames = new LinkedList<>(); - getBlocks(args, blockNames); - - blocks = blockNames.stream() - .map(blockName -> ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName))) - .map(Block::getDefaultState).collect(Collectors.toList()); - - blocks.addAll(OreSpawn.API - .getDimensionDefaultReplacements(player.getEntityWorld().provider.getDimension()) - .stream().collect(Collectors.toList())); - final List overburden = Arrays - .asList("minecraft:dirt", "minecraft:sand", "minecraft:gravel", "minecraft:grass", - "minecraft:sandstone", "minecraft:red_sandstone") - .stream() - .map(blockName -> ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName))) - .map(Block::getDefaultState).collect(Collectors.toList()); - - clearBlocks(chunkPos, blocks, overburden, flagClassic, player); - - player.sendStatusMessage( - new TextComponentString("chunk " + chunkPos.toString() + " cleared"), true); - } - - private void clearBlocks(final ChunkPos chunkPos, final List blocks, - final List overburden, final boolean flagClassic, - final EntityPlayer player) { - for (int x = chunkPos.getXStart(); x <= chunkPos.getXEnd(); x++) { - for (int y = 256; y >= 0; y--) { - for (int z = chunkPos.getZStart(); z <= chunkPos.getZEnd(); z++) { - final BlockPos pos = new BlockPos(x, y, z); - final IBlockState block = player.getEntityWorld().getBlockState(pos); - removeIfBlocks(player, pos, block, blocks, overburden, !flagClassic); - removeIfFluid(pos, player); - } - } - } - } - - private void removeIfFluid(final BlockPos pos, final EntityPlayer player) { - if (player.getEntityWorld().getBlockState(pos).getMaterial().isLiquid()) { - final IBlockState bs = player.getEntityWorld().getBlockState(pos); - - if (bs.getMaterial().isLiquid()) { - player.getEntityWorld().setBlockToAir(pos); - } - } - } - - private void removeIfBlocks(final EntityPlayer player, final BlockPos pos, - final IBlockState block, final List blocks, - final List overburden, final boolean flagClassic) { - if (blocks.contains(block) - || ((pos.getY() >= 64 && overburden.contains(block)) && flagClassic)) { - player.getEntityWorld().setBlockToAir(pos); - } - } - - private void getBlocks(final String[] args, final List blockNames) { - if (args.length > 0) { - switch (args[0].toLowerCase()) { - case "viewores": - blockNames.addAll(stoneVariants); - blockNames.addAll(dirtVariants); - blockNames.addAll(otherVariants); - break; - - case "dirtandgravel": - blockNames.add(STONE_ID); - blockNames.addAll(dirtVariants); - blockNames.addAll(otherVariants); - break; - - case "classic": - blockNames.add(Blocks.STONE.getRegistryName().toString()); - blockNames.add(Blocks.NETHERRACK.getRegistryName().toString()); - blockNames.add(Blocks.END_STONE.getRegistryName().toString()); - blockNames - .addAll(OreDictionary.getOres("stone").stream().map(ItemStack::getItem) - .map(Block::getBlockFromItem).map(Block::getRegistryName) - .map(ResourceLocation::toString).collect(Collectors.toList())); - break; - - default: - blockNames.addAll(baseStones); - } - } else { - blockNames.addAll(baseStones); - } - } - - @Override - public int compareTo(final ICommand command) { - return this.getName().compareTo(command.getName()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/commands/DumpBiomesCommand.java b/src/main/java/com/mcmoddev/orespawn/commands/DumpBiomesCommand.java deleted file mode 100644 index fa9f2cc..0000000 --- a/src/main/java/com/mcmoddev/orespawn/commands/DumpBiomesCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.mcmoddev.orespawn.commands; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringEscapeUtils; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonPrimitive; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.world.biome.Biome; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class DumpBiomesCommand extends CommandBase { - - @Override - public String getName() { - return "dumpbiomes"; - } - - @Override - public String getUsage(final ICommandSender sender) { - return "/dumpbiomes"; - } - - @Override - public void execute(final MinecraftServer server, final ICommandSender sender, - final String[] args) throws CommandException { - JsonArray array = new JsonArray(); - - for (final Biome biome : ForgeRegistries.BIOMES) { - array.add(new JsonPrimitive(biome.getRegistryName().toString())); - } - - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - final String json = gson.toJson(array); - - try { - FileUtils.writeStringToFile(new File(".", "biome_dump.json"), - StringEscapeUtils.unescapeJson(json), StandardCharsets.UTF_8); - } catch (final IOException e) { - throw new CommandException("Failed to save the json file"); - } - - sender.sendMessage(new TextComponentString("Done")); - } - - @Override - public int compareTo(final ICommand command) { - return this.getName().compareTo(command.getName()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/commands/WriteConfigsCommand.java b/src/main/java/com/mcmoddev/orespawn/commands/WriteConfigsCommand.java deleted file mode 100644 index 4d1dc06..0000000 --- a/src/main/java/com/mcmoddev/orespawn/commands/WriteConfigsCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.mcmoddev.orespawn.commands; - -import com.mcmoddev.orespawn.json.OreSpawnWriter; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommandSender; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.text.TextComponentString; - -public class WriteConfigsCommand extends CommandBase { - - @Override - public String getName() { - return "osSaveConfigs"; - } - - @Override - public String getUsage(final ICommandSender sender) { - return "/osSaveConfigs"; - } - - @Override - public void execute(final MinecraftServer server, final ICommandSender sender, - final String[] args) throws CommandException { - sender.sendMessage(new TextComponentString( - "Forcing configs as OreSpawn sees them to be written to disk")); - OreSpawnWriter.saveConfigs(); - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java index 7f4f2c5..925c426 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Config.java @@ -1,170 +1,7 @@ package com.mcmoddev.orespawn.data; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import org.apache.commons.io.FileUtils; - -import com.google.common.collect.ImmutableList; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonParser; -import com.mcmoddev.orespawn.OreSpawn; - -import net.minecraft.crash.CrashReport; -import net.minecraftforge.common.config.Configuration; - public class Config { - - private static Configuration configuration; - - private Config() { - } - - public static void loadConfig() { - configuration = new Configuration(new File(Constants.CONFIG_FILE)); - - // Load our Boolean Values - boolVals.put(Constants.RETROGEN_KEY, configuration.getBoolean(Constants.RETROGEN_KEY, - Configuration.CATEGORY_GENERAL, false, - "Do we have Retrogen active and generating anything different from the last run in already existing chunks ?")); - boolVals.put(Constants.FORCE_RETROGEN_KEY, - configuration.getBoolean(Constants.FORCE_RETROGEN_KEY, - Configuration.CATEGORY_GENERAL, false, - "Force all chunks to retrogen regardless of anything else")); - boolVals.put(Constants.REPLACE_VANILLA_OREGEN, - configuration.getBoolean(Constants.REPLACE_VANILLA_OREGEN, - Configuration.CATEGORY_GENERAL, false, - "Replace vanilla ore-generation entirely")); - boolVals.put(Constants.FLAT_BEDROCK, - configuration.getBoolean(Constants.FLAT_BEDROCK, Configuration.CATEGORY_GENERAL, - false, "Flatten the bedrock during world generation")); - boolVals.put(Constants.RETRO_BEDROCK, configuration.getBoolean(Constants.RETRO_BEDROCK, - Configuration.CATEGORY_GENERAL, false, "Retroactively flatten bedrock")); - boolVals.put(Constants.REPLACE_ALL, configuration.getBoolean(Constants.REPLACE_ALL, Configuration.CATEGORY_GENERAL, false, "Replace all ore generation")); - intVals.put(Constants.BEDROCK_LAYERS, - configuration.getInt(Constants.BEDROCK_LAYERS, Configuration.CATEGORY_GENERAL, 1, 1, - 4, "How thick should the shell of bedrock be?")); - knownKeys.add(Constants.RETROGEN_KEY); - knownKeys.add(Constants.FORCE_RETROGEN_KEY); - knownKeys.add(Constants.REPLACE_VANILLA_OREGEN); - knownKeys.add(Constants.KNOWN_MODS); - knownKeys.add(Constants.FLAT_BEDROCK); - knownKeys.add(Constants.RETRO_BEDROCK); - knownKeys.add(Constants.BEDROCK_LAYERS); - - loadExtractedConfigs(); - } - - private static void loadExtractedConfigs() { - final Path p = FileSystems.getDefault().getPath("config", "orespawn3", "sysconf", - "known-configs.json"); - - if (!p.toFile().exists()) { - return; - } - - final File in = p.toFile(); - String rawData; - - try { - rawData = FileUtils.readFileToString(in, Charset.defaultCharset()); - } catch (IOException e) { - return; - } - - if (rawData.isEmpty()) { - return; - } - - final JsonArray data = new JsonParser().parse(rawData).getAsJsonArray(); - data.forEach(item -> addKnownMod(item.getAsString())); - } - - public static List getKnownMods() { - return ImmutableList.copyOf(extractedConfigs); - } - public static void addKnownMod(final String modId) { - extractedConfigs.add(modId); - } - - public static boolean getBoolean(final String keyname) { - if (knownKeys.contains(keyname) && boolVals.containsKey(keyname)) { - return boolVals.get(keyname); - } - - return false; - } - - public static String getString(final String keyname) { - if (knownKeys.contains(keyname) && stringVals.containsKey(keyname)) { - return stringVals.get(keyname); - } - - return ""; - } - - public static int getInt(final String keyname) { - if (knownKeys.contains(keyname) && intVals.containsKey(keyname)) { - return intVals.get(keyname); - } - - return 0; - } - - public static float getFloat(final String keyname) { - if (knownKeys.contains(keyname) && floatVals.containsKey(keyname)) { - return floatVals.get(keyname); - } - - return 0.0f; + // TODO: Complete } - - public static void saveConfig() { - if (!extractedConfigs.isEmpty()) { - saveKnownConfigs(); - } - - configuration.save(); - } - - private static void saveKnownConfigs() { - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - final Path p = FileSystems.getDefault().getPath("config", "orespawn3", "sysconf", - "known-configs.json"); - - if (!p.toFile().getParentFile().exists()) { - p.toFile().mkdirs(); - } - - final File in = p.toFile(); - - final JsonArray data = new JsonArray(); - - extractedConfigs.forEach(data::add); - - try { - FileUtils.writeStringToFile(in, gson.toJson(data), Charset.defaultCharset()); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed saving list of already extracted mod configs"); - report.getCategory().addCrashSection("OreSpawn Version", Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } - - private static final HashMap boolVals = new HashMap<>(); - private static final HashMap stringVals = new HashMap<>(); - private static final HashMap intVals = new HashMap<>(); - private static final HashMap floatVals = new HashMap<>(); - private static final List knownKeys = new ArrayList<>(); - private static final List extractedConfigs = new ArrayList<>(); } diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index 5509dde..5b5ebeb 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -4,106 +4,14 @@ import java.nio.file.Paths; public class Constants { - - public static final String MODID = "orespawn"; - public static final String NAME = "MMD OreSpawn"; - public static final String VERSION = "3.3.1"; - public static final String RETROGEN_KEY = "Retrogen"; - public static final String CONFIG_FILE = "config/orespawn.cfg"; - public static final String FORCE_RETROGEN_KEY = "Force Retrogen"; - public static final String CHUNK_TAG_NAME = "MMD OreSpawn Data"; - public static final String ORE_TAG = "ores"; - public static final String FEATURES_TAG = "features"; - public static final String REPLACE_VANILLA_OREGEN = "Replace Vanilla Oregen"; - public static final String OVERWORLD = "overworld"; - public static final String THE_OVERWORLD = "the overworld"; - public static final String NETHER = "nether"; - public static final String THE_NETHER = "the nether"; - public static final String END = "end"; - public static final String THE_END = "the end"; - public static final String DEFAULT_GEN = "default"; - public static final String VEIN_GEN = "vein"; - public static final String NORMAL_CLOUD = "normal-cloud"; - public static final String CLUSTERS = "clusters"; + public static final String VERSION = "4.0.0"; public static final String CRASH_SECTION = "OreSpawn Version"; - public static final String KNOWN_MODS = "already-extracted"; - public static final String RETRO_BEDROCK = "Retrogen Flat Bedrock"; - public static final String FLAT_BEDROCK = "Flatten Bedrock"; - public static final String RETRO_BEDROCK_TAG = "retro-bedrock"; - public static final String BEDROCK_LAYERS = "Bedrock Thickness"; - public static final String ORESPAWN_VERSION_CRASH_MESSAGE = "OreSpawn Version"; - public static final String PRECISION = "precision"; - public static final Path CONFDIR = Paths.get(FileBits.CONFIG_DIR, FileBits.OS3); - public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS3, FileBits.SYSCONF); - public static final String REPLACE_ALL = "Replace All Generation"; - - public final class FormatBits { - - private FormatBits() { - } - - public static final String MAX_SPREAD = "maxSpread"; - public static final String MEDIAN_SIZE = "medianSize"; - public static final String MIN_HEIGHT = "minHeight"; - public static final String MAX_HEIGHT = "maxHeight"; - public static final String VARIATION = "variation"; - public static final String FREQUENCY = "frequency"; - public static final String NODE_SIZE = "size"; - public static final String ATTEMPTS = "attempts"; - public static final String LENGTH = "length"; - public static final String NODE_COUNT = "numObjects"; - public static final String ATTEMPTS_MIN = "minAttempts"; - public static final String ATTEMPTS_MAX = "maxAttempts"; - public static final String STARTINGFACE = "startFacing"; - public static final String FLUID = "fluid"; - } - - public final class FileBits { - - private FileBits() { - } - + public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4, FileBits.SYSCONF); + public static class FileBits { public static final String CONFIG_DIR = "config"; - public static final String OS3 = "orespawn3"; + public static final String OS4 = "mmd-orespawn-4"; public static final String SYSCONF = "sysconf"; public static final String PRESETS = "presets.json"; - } - - public final class ConfigNames { - - private ConfigNames() { - } - - public static final String DEFAULT = "default"; - public static final String STATE_NORMAL = "normal"; - public static final String DIMENSIONS = "dimensions"; - public static final String CHANCE = "chance"; - public static final String METADATA = "metaData"; - public static final String BIOMES = "biomes"; - public static final String STATE = "state"; - public static final String REPLACEMENT = "replaces"; - public static final String FEATURE = "feature"; - public static final String PARAMETERS = "parameters"; - public static final String FILE_VERSION = "version"; - public static final String BLOCK = "blocks"; - public static final String SPAWNS = "spawns"; - public static final String RETROGEN = "retrogen"; - public static final String ENABLED = "enabled"; - public static final String NAME = "name"; - public static final String WHITELIST = "includes"; - public static final String BLACKLIST = "excludes"; - public static final String PRESETS = "presets"; - - public final class DefaultFeatureProperties { - - private DefaultFeatureProperties() { - } - - public static final String SIZE = "size"; - public static final String VARIATION = "variation"; - public static final String FREQUENCY = "frequency"; - public static final String MAXHEIGHT = "maxHeight"; - public static final String MINHEIGHT = "minHeight"; - } + public static final String ALLOWED_MODS = "active_mods.json"; } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/FeatureRegistry.java b/src/main/java/com/mcmoddev/orespawn/data/FeatureRegistry.java deleted file mode 100644 index 603ef37..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/FeatureRegistry.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringEscapeUtils; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.IFeature; - -import net.minecraft.crash.CrashReport; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.registries.IForgeRegistry; -import net.minecraftforge.registries.RegistryBuilder; - -public class FeatureRegistry { - - private static final String ORE_SPAWN_VERSION = "OreSpawn Version"; - private static final IForgeRegistry registry = new RegistryBuilder() - .setName(new ResourceLocation("orespawn", "feature_registry")).setType(IFeature.class) - .setMaxID(4096) // 12 bits should be enough... hell, 8 bits would be, IMNSHO - .create(); - - public FeatureRegistry() { - // - } - - public Map getFeatures() { - final Map tempMap = new TreeMap<>(); - registry.getEntries().stream() - .forEach(e -> tempMap.put(e.getKey().toString(), e.getValue())); - - return Collections.unmodifiableMap(tempMap); - } - - public String getFeatureName(final IFeature feature) { - return feature.getRegistryName().toString(); - } - - public IFeature getFeature(final String name) { - return getFeature(new ResourceLocation(name)); - } - - public IFeature getFeature(final ResourceLocation featureResourceLocation) { - final ResourceLocation defaultGen = new ResourceLocation(Constants.DEFAULT_GEN); - if (registry.containsKey(featureResourceLocation)) { - return registry.getValue(featureResourceLocation); - } else { - return registry.getValue(defaultGen); - } - } - - public boolean hasFeature(final String name) { - return hasFeature(new ResourceLocation(name)); - } - - public boolean hasFeature(final ResourceLocation featureResourceLocation) { - return registry.containsKey(featureResourceLocation); - } - - public boolean hasFeature(final IFeature feature) { - return registry.containsKey(feature.getRegistryName()); - } - - public void addFeature(final String name, final IFeature feature) { - feature.setRegistryName(new ResourceLocation("orespawn", name)); - registry.register(feature); - } - - public void addFeature(final JsonObject entry) { - addFeature(entry.get("name").getAsString(), entry.get("class").getAsString()); - } - - public void addFeature(final String name, final String className) { - final IFeature feature = getInstance(className); - - if (feature != null && !hasFeature(name)) { - addFeature(name, feature); - } - } - - private static IFeature getInstance(final String className) { - Class featureClazz; - Constructor featureCons; - IFeature feature; - - try { - featureClazz = Class.forName(className); - featureCons = featureClazz.getConstructor(); - feature = (IFeature) featureCons.newInstance(); - } catch (final Exception e) { - final CrashReport report = CrashReport.makeCrashReport(e, - "Failed to load and instantiate an instance of the feature generator named " - + className + " that was specified as a feature generator"); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - return null; - } - - return feature; - } - - public void loadFeaturesFile(final File file) { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonArray elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - final CrashReport report = CrashReport.makeCrashReport(e, - "Failed reading config " + file.getName()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - return; - } - - elements = parser.parse(rawJson).getAsJsonArray(); - - for (final JsonElement elem : elements) { - addFeature(elem.getAsJsonObject()); - } - } - - public void writeFeatures(final File file) { - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - - final JsonArray root = new JsonArray(); - - registry.getEntries().stream().map(ent -> { - final JsonObject e = new JsonObject(); - e.addProperty("name", ent.getKey().getPath()); - e.addProperty("class", ent.getValue().getClass().getName()); - return e; - }).forEach(root::add); - - final String json = gson.toJson(root); - - try { - FileUtils.writeStringToFile(file, StringEscapeUtils.unescapeJson(json), - StandardCharsets.UTF_8); - } catch (final IOException e) { - final CrashReport report = CrashReport.makeCrashReport(e, - "Failed writing config " + file.getName()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java new file mode 100644 index 0000000..7a26096 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java @@ -0,0 +1,24 @@ +package com.mcmoddev.orespawn.data; + +public class OS4BlockData { + private final String blockIdentifier; + private final String blockState; + + public OS4BlockData(final String blockName, final String state) { + this.blockIdentifier = blockName; + this.blockState = state==null?"":state; + } + + public final String getBlock() { + String fmt = this.blockState.length()==0?"%s%s":"%s[%s]"; + return String.format(fmt, this.blockIdentifier, this.blockState); + } + + public final String getBlockName() { + return this.blockIdentifier; + } + + public final String getBlockState() { + return this.blockState; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/PresetsStorage.java b/src/main/java/com/mcmoddev/orespawn/data/PresetsStorage.java deleted file mode 100644 index d0d948c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/PresetsStorage.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; -import com.mcmoddev.orespawn.OreSpawn; - -import net.minecraft.crash.CrashReport; - -public class PresetsStorage { - - private final Map> storage; - private static final String ORE_SPAWN_VERSION = "OreSpawn Version"; - - public PresetsStorage() { - storage = new TreeMap<>(); - } - - public void setSymbolSection(final String sectionName, final String itemName, - final JsonElement value) { - final Map temp = storage.getOrDefault(sectionName, - new HashMap()); - temp.put(itemName, value); - storage.put(sectionName, temp); - } - - public JsonElement getSymbolSection(final String sectionName, final String itemName) { - if (storage.containsKey(sectionName) && storage.get(sectionName).containsKey(itemName)) { - return storage.get(sectionName).get(itemName); - } else { - return new JsonPrimitive(itemName); - } - } - - public void copy(final PresetsStorage dest) { - storage.entrySet().stream().forEach(ensm -> { - final String section = ensm.getKey(); - ensm.getValue().entrySet().forEach( - ensje -> dest.setSymbolSection(section, ensje.getKey(), ensje.getValue())); - }); - } - - public void clear() { - this.storage.clear(); - } - - public void load(final Path inputFile) { - final JsonParser p = new JsonParser(); - JsonElement parsed = null; - - try (BufferedReader r = Files.newBufferedReader(inputFile)) { - parsed = p.parse(r); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed reading presets from" + inputFile.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } catch (final JsonIOException | JsonSyntaxException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "JSON Parsing Error in " + inputFile.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - - if (parsed != null) { - parsed.getAsJsonObject().entrySet().stream().forEach(entry -> { - final String section = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet().stream().forEach( - sect -> this.setSymbolSection(section, sect.getKey(), sect.getValue())); - }); - } - } - - public JsonElement get(final String asString) { - final Pattern p = Pattern.compile("\\$\\.(\\w+)\\.(\\w+)"); - final Matcher m = p.matcher(asString); - if (m.matches()) { - return this.getSymbolSection(m.group(1), m.group(2)); - } - return new JsonPrimitive("Unknown Variable " + asString); - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/data/ReplacementsRegistry.java deleted file mode 100644 index 657ae61..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/ReplacementsRegistry.java +++ /dev/null @@ -1,265 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; -import org.apache.commons.io.FileUtils; - -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.impl.os3.ReplacementEntry; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.crash.CrashReport; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.registry.ForgeRegistries; -import net.minecraftforge.oredict.OreDictionary; -import net.minecraftforge.registries.IForgeRegistryModifiable; -import net.minecraftforge.registries.RegistryBuilder; - -public class ReplacementsRegistry { - - private static final String ORE_SPAWN_VERSION = "OreSpawn Version"; - private static final IForgeRegistryModifiable registry = (IForgeRegistryModifiable) new RegistryBuilder() - .setName(new ResourceLocation("orespawn", "replacements_registry")).allowModification() - .setType(IReplacementEntry.class).setMaxID(65535) // 16 bits should be enough... - .create(); - - public ReplacementsRegistry() { - // - } - - public Map getReplacements() { - return ImmutableMap.copyOf(registry.getEntries()); - } - - /** - * - * @param dimension - * @return - */ - /* - * @deprecated (no, it isn't - why was it flagged this to begin with ?) - */ - // @Deprecated - @SuppressWarnings("deprecation") - public List getDimensionDefault(final int dimension) { - final String[] names = { - "minecraft:netherrack", "minecraft:stone", "minecraft:end_stone" - }; - - final List baseRv = new ArrayList<>(); - - if (dimension < -1 || dimension > 1 || dimension == 0) { - for (final ItemStack iS : OreDictionary.getOres("stone")) { - baseRv.add(Block.getBlockFromItem(iS.getItem()).getStateFromMeta(iS.getMetadata())); - } - - return baseRv; - } - - baseRv.addAll(Arrays.asList(ForgeRegistries.BLOCKS - .getValue(new ResourceLocation(names[dimension + 1])).getDefaultState())); - return baseRv; - } - - public IReplacementEntry getReplacement(final String name) { - final ResourceLocation act = new ResourceLocation( - name.contains(":") ? name : String.format(Locale.ENGLISH, "orespawn:%s", name)); - - if (registry.containsKey(act)) { - return registry.getValue(act); - } else { - return registry.getValue(new ResourceLocation("orespawn:default")); - } - } - - public void addBlock(final String name, final String blockName, final String blockState) { - IBlockState b; - try { - b = StateUtil.deserializeState( - ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName)), blockState); - } catch (Exception e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%u)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - b = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName)).getDefaultState(); - } - addBlock(name, b); - } - - public Map> getBlocks() { - final Map> tempMap = new TreeMap<>(); - registry.getEntries().stream() - .forEach(e -> tempMap.put(e.getKey().toString(), e.getValue().getEntries())); - - return Collections.unmodifiableMap(tempMap); - } - - public void addReplacement(final IReplacementEntry replacement) { - registry.register(replacement); - } - - public void addBlock(final String name, final IBlockState state) { - ResourceLocation regName = new ResourceLocation(name); - if (registry.containsKey(regName)) { - final IReplacementEntry old = registry.getValue(regName); - IReplacementEntry newRE; - List oldList = old.getEntries(); - oldList.add(state); - newRE = new ReplacementEntry(name, oldList); - registry.remove(regName); - newRE.setRegistryName(regName); - registry.register(newRE); - return; - } - - final IReplacementEntry r = new ReplacementEntry(name, state); - registry.register(r); - } - - @SuppressWarnings("deprecation") - public void loadFile(final Path file) { - final JsonParser parser = new JsonParser(); - JsonObject elements; - String rawJson; - - - try { - rawJson = FileUtils.readFileToString(file.toFile(), Charset.defaultCharset()); - } catch (IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed reading config " + file.getFileName()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - return; - } - - elements = parser.parse(rawJson).getAsJsonObject(); - elements.entrySet().stream().forEach(elem -> { - final String entName = elem.getKey(); - final JsonArray entries = elem.getValue().getAsJsonArray(); - final List blocks = new LinkedList<>(); - entries.forEach( e -> { - final JsonObject asObj = e.getAsJsonObject(); - final String blockName = asObj.get(Constants.ConfigNames.NAME).getAsString() - .toLowerCase(); - - // is this an OreDictionary entry ? - if (blockName.startsWith("ore:")) { - loadOreDict(blockName, blocks); - } else { - final ResourceLocation blockRL = new ResourceLocation(blockName); - final Block theBlock = ForgeRegistries.BLOCKS.getValue(blockRL); - if (asObj.has(Constants.ConfigNames.METADATA)) { - // has metadata - final int meta = asObj.get(Constants.ConfigNames.METADATA).getAsInt(); - blocks.add(theBlock.getStateFromMeta(meta)); - } else if (asObj.has(Constants.ConfigNames.STATE)) { - // has a state - loadBlockState(blockName, asObj, blocks); - } else { - // use the default state - blocks.add(theBlock.getDefaultState()); - } - } - }); - - final ResourceLocation checkName= new ResourceLocation("orespawn", entName); - if (registry.containsKey(checkName)) { - final List existingReps = getReplacement(checkName.toString()).getEntries(); - blocks.addAll(existingReps); - } - final IReplacementEntry replacer = new ReplacementEntry(checkName.toString(), blocks); - registry.register(replacer); - }); - } - - private void loadBlockState(String blockName, JsonObject asObj, List blocks) { - final ResourceLocation blockRL = new ResourceLocation(blockName); - final Block theBlock = ForgeRegistries.BLOCKS.getValue(blockRL); - final String state = asObj.get(Constants.ConfigNames.STATE).getAsString(); - IBlockState b; - try { - b = StateUtil.deserializeState(theBlock, state); - } catch (Exception e1) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem1: e1.getStackTrace()) p.append(String.format("%s.%s (%s:%d)\n", elem1.getClassName(), elem1.getMethodName(), elem1.getFileName(), elem1.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception loading block with state [%s]: %s\n%s", state, e1.getMessage(), p.toString())); - b = theBlock.getDefaultState(); - } - blocks.add(b); - } - - @SuppressWarnings("deprecation") - private void loadOreDict(String blockName, List blocks) { - // yes, it is - final String oreDictName = blockName.split(":")[1]; - OreDictionary.getOres(oreDictName).forEach(iS -> { - if (iS.getMetadata() != 0) { - blocks.add(Block.getBlockFromItem(iS.getItem()) - .getStateFromMeta(iS.getMetadata())); - } else { - blocks.add(Block.getBlockFromItem(iS.getItem()).getDefaultState()); - } - }); - } - - public void saveFile(final String modName) { - final JsonObject outs = new JsonObject(); - - registry.getEntries().stream().filter(ent -> ent.getKey().getNamespace().equals(modName)) - .forEach(ent -> { - final JsonArray entry = new JsonArray(); - final IReplacementEntry workVal = ent.getValue(); - workVal.getEntries().stream().forEach(bs -> { - final JsonObject block = new JsonObject(); - block.addProperty(Constants.ConfigNames.BLOCK, - bs.getBlock().getRegistryName().toString()); - if (!bs.toString().matches("\\[normal\\]")) { - block.addProperty(Constants.ConfigNames.STATE, - bs.toString().replaceAll("[\\[\\]]", "")); - } - entry.add(block); - }); - outs.add(ent.getKey().toString(), entry); - }); - final Path p = Paths.get("config", "orespawn3", "sysconfig", - String.format(Locale.ENGLISH, "replacements-%s.json", modName)); - try (final BufferedWriter w = Files.newBufferedWriter(p)) { - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - final String ov = gson.toJson(outs); - w.write(ov); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, String - .format(Locale.ENGLISH, "Failed writing replacements file %s", p.toAbsolutePath().toString())); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } - - public boolean has(final ResourceLocation resourceLocation) { - return registry.containsKey(resourceLocation); - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/VanillaOres.java b/src/main/java/com/mcmoddev/orespawn/data/VanillaOres.java deleted file mode 100644 index e04934b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/VanillaOres.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.api.os3.OS3API; -import com.mcmoddev.orespawn.api.plugin.IOreSpawnPlugin; -import com.mcmoddev.orespawn.api.plugin.OreSpawnPlugin; - -@OreSpawnPlugin(modid = "orespawn", resourcePath = "configs") -public class VanillaOres implements IOreSpawnPlugin { - - @Override - public void register(final OS3API apiInterface) { - // nothing for us to do - all of our ores are in the - // jar and the code handles that - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/ClusterGenerator.java b/src/main/java/com/mcmoddev/orespawn/impl/features/ClusterGenerator.java deleted file mode 100644 index e6b8d54..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/ClusterGenerator.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.mcmoddev.orespawn.impl.features; - -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public class ClusterGenerator extends FeatureBase implements IFeature { - - private ClusterGenerator(final Random rand) { - super(rand); - } - - public ClusterGenerator() { - this(new Random()); - } - - private class SpawnParameters { - public final int clusterSize; - public final int variance; - public final int clusterCount; - public final int maxSpread; - public final int minHeight; - public final int maxHeight; - - SpawnParameters(final int clusterSize, final int variance, final int clusterCount, - final int maxSpread, final int minHeight, final int maxHeight) { - this.clusterSize = clusterSize; - this.variance = variance; - this.clusterCount = clusterCount; - this.maxSpread = maxSpread; - this.minHeight = minHeight; - this.maxHeight = maxHeight; - } - } - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos posIn) { - final ChunkPos pos = posIn; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - mergeDefaults(params, getDefaultParameters()); - - runCache(chunkX, chunkZ, world, spawnData); - - // now to ore spawn - - final int blockX = chunkX * 16 + 8; - final int blockZ = chunkZ * 16 + 8; - - final int maxSpread = params.get(Constants.FormatBits.MAX_SPREAD).getAsInt(); - final int minHeight = params.get(Constants.FormatBits.MIN_HEIGHT).getAsInt(); - final int maxHeight = params.get(Constants.FormatBits.MAX_HEIGHT).getAsInt(); - final int variance = params.get(Constants.FormatBits.VARIATION).getAsInt(); - final int frequency = params.get(Constants.FormatBits.FREQUENCY).getAsInt(); - final int triesMin = params.get(Constants.FormatBits.ATTEMPTS_MIN).getAsInt(); - final int triesMax = params.get(Constants.FormatBits.ATTEMPTS_MAX).getAsInt(); - final int clusterSize = params.get(Constants.FormatBits.NODE_SIZE).getAsInt(); - final int clusterCount = params.get(Constants.FormatBits.NODE_COUNT).getAsInt(); - - int tries; - - if (triesMax == triesMin) { - tries = triesMax; - } else { - tries = random.nextInt(triesMax - triesMin) + triesMin; - } - - while (tries > 0) { - if (this.random.nextInt(100) <= frequency) { - final int xRand = random.nextInt(16); - final int zRand = random.nextInt(16); - - final int x = blockX + xRand - (maxSpread / 2); - final int y = random.nextInt(maxHeight - minHeight) + minHeight; - final int z = blockZ + zRand - (maxSpread / 2); - - spawnCluster(new SpawnParameters(clusterSize, variance, clusterCount, maxSpread, minHeight, maxHeight), - spawnData, world, new BlockPos(x, y, z)); - } - - tries--; - } - } - - private void spawnCluster(final SpawnParameters params, - final ISpawnEntry spawnData, final World world, final BlockPos pos) { - // spawn a cluster at the center, then a bunch around the outside... - int r = params.clusterSize - params.variance; - - if (params.variance > 0) { - r += this.random.nextInt(2 * params.variance) - params.variance; - } - - spawnChunk(world, pos, spawnData, r); - - int count = this.random.nextInt(params.clusterCount - 1); // always at least the first, but vary - // inside that - - if (params.variance > 0) { - count += this.random.nextInt(2 * params.variance) - params.variance; - } - - while (count >= 0) { - r = params.clusterSize - params.variance; - - if (params.variance > 0) { - r += this.random.nextInt(2 * params.variance) - params.variance; - } - - final int radius = params.maxSpread / 2; - - final int xp = getPoint(-radius, radius, 0); - final int yp = getPoint(params.minHeight, params.maxHeight, (params.maxHeight - params.minHeight) / 2); - final int zp = getPoint(-radius, radius, 0); - - final BlockPos p = pos.add(xp, yp, zp); - spawnChunk(world, p, spawnData, r); - - count -= r; - } - } - - private void spawnChunk(final World world, final BlockPos pos, final ISpawnEntry spawnData, - final int quantity) { - int count = quantity; - final int lutType = (quantity < 8) ? offsetIndexRef_small.length : offsetIndexRef.length; - final int[] lut = (quantity < 8) ? offsetIndexRef_small : offsetIndexRef; - final Vec3i[] offs = new Vec3i[lutType]; - - System.arraycopy((quantity < 8) ? offsets_small : offsets, 0, offs, 0, lutType); - - final int dimension = world.provider.getDimension(); - - if (quantity < 27) { - final int[] scrambledLUT = new int[lutType]; - System.arraycopy(lut, 0, scrambledLUT, 0, scrambledLUT.length); - scramble(scrambledLUT, this.random); - int z = 0; - - while (count > 0) { - final IBlockState oreBlock = spawnData.getBlocks().getRandomBlock(random); - if (oreBlock.getBlock().equals(net.minecraft.init.Blocks.AIR)) return; - - if (!spawn(oreBlock, world, pos.add(offs[scrambledLUT[--count]]), dimension, true, - spawnData)) { - count++; - z++; - scramble(scrambledLUT, this.random); // rescramble the LUT - } else { - z = 0; - } - - if (z > 5) { - count--; - z = 0; - OreSpawn.LOGGER.warn("Unable to place block for chunk after 5 tries"); - } - } - - return; - } - - doSpawnFill(this.random.nextBoolean(), count, spawnData, world, pos); - } - - private void doSpawnFill(final boolean nextBoolean, final int quantity, - final ISpawnEntry spawnData, final World world, final BlockPos pos) { - final int count = quantity; - final double radius = Math.pow(quantity, 1.0 / 3.0) * (3.0 / 4.0 / Math.PI) + 2; - final int rSqr = (int) (radius * radius); - if (nextBoolean) { - spawnMungeNE(world, pos, rSqr, radius, spawnData, count); - } else { - spawnMungeSW(world, pos, rSqr, radius, spawnData, count); - } - } - - @Override - public void setRandom(final Random rand) { - this.random = rand; - } - - @Override - public JsonObject getDefaultParameters() { - final JsonObject defParams = new JsonObject(); - defParams.addProperty(Constants.FormatBits.MAX_SPREAD, 16); - defParams.addProperty(Constants.FormatBits.NODE_SIZE, 8); - defParams.addProperty(Constants.FormatBits.NODE_COUNT, 8); - defParams.addProperty(Constants.FormatBits.MIN_HEIGHT, 8); - defParams.addProperty(Constants.FormatBits.MAX_HEIGHT, 24); - defParams.addProperty(Constants.FormatBits.VARIATION, 4); - defParams.addProperty(Constants.FormatBits.FREQUENCY, 25); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MIN, 4); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MAX, 8); - return defParams; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/DefaultFeatureGenerator.java b/src/main/java/com/mcmoddev/orespawn/impl/features/DefaultFeatureGenerator.java deleted file mode 100644 index 37cfcab..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/DefaultFeatureGenerator.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.mcmoddev.orespawn.impl.features; - -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public class DefaultFeatureGenerator extends FeatureBase implements IFeature { - - public DefaultFeatureGenerator() { - super(new Random()); - } - - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos chunkPos) { - final ChunkPos pos = chunkPos; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - mergeDefaults(params, getDefaultParameters()); - - runCache(chunkX, chunkZ, world, spawnData); - - // now to ore spawn - - final int blockX = chunkX * 16 + 8; - final int blockZ = chunkZ * 16 + 8; - - final int minY = params.get(Constants.FormatBits.MIN_HEIGHT).getAsInt(); - final int maxY = params.get(Constants.FormatBits.MAX_HEIGHT).getAsInt(); - final int vari = params.get(Constants.FormatBits.VARIATION).getAsInt(); - final float freq = params.get(Constants.FormatBits.FREQUENCY).getAsFloat(); - final int size = params.get(Constants.FormatBits.NODE_SIZE).getAsInt(); - - if (freq >= 1) { - for (int i = 0; i < freq; i++) { - final int x = blockX + random.nextInt(16); - final int y = random.nextInt(maxY - minY) + minY; - final int z = blockZ + random.nextInt(16); - - final int r; - - if (vari > 0) { - r = random.nextInt(2 * vari) - vari; - } else { - r = 0; - } - spawnOre(world, spawnData, new BlockPos(x, y, z), size + r); - } - } else if (random.nextFloat() < freq) { - final int x = blockX + random.nextInt(8); - final int y = random.nextInt(maxY - minY) + minY; - final int z = blockZ + random.nextInt(8); - final int r; - - if (vari > 0) { - r = random.nextInt(2 * vari) - vari; - } else { - r = 0; - } - - spawnOre(world, spawnData, new BlockPos(x, y, z), size + r); - } - - } - - private void spawnOre(final World world, final ISpawnEntry spawnData, final BlockPos pos, - final int quantity) { - int count = quantity; - final int lutType = (quantity < 8) ? offsetIndexRef_small.length : offsetIndexRef.length; - final int[] lut = (quantity < 8) ? offsetIndexRef_small : offsetIndexRef; - final Vec3i[] offs = new Vec3i[lutType]; - - System.arraycopy((quantity < 8) ? offsets_small : offsets, 0, offs, 0, lutType); - - if (quantity < 27) { - final int[] scrambledLUT = new int[lutType]; - System.arraycopy(lut, 0, scrambledLUT, 0, scrambledLUT.length); - scramble(scrambledLUT, this.random); - - while (count > 0) { - final IBlockState oreBlock = spawnData.getBlocks().getRandomBlock(random); - if (oreBlock.getBlock().equals(net.minecraft.init.Blocks.AIR)) return; - final BlockPos target = pos.add(offs[scrambledLUT[--count]]); - spawn(oreBlock, world, target, world.provider.getDimension(), true, spawnData); - } - - return; - } - - doSpawnFill(this.random.nextBoolean(), count, world, spawnData, pos); - } - - private void doSpawnFill(final boolean nextBoolean, final int quantity, final World world, - final ISpawnEntry spawnData, final BlockPos pos) { - final int count = quantity; - final double radius = Math.pow(quantity, 1.0 / 3.0) * (3.0 / 4.0 / Math.PI) + 2; - final int rSqr = (int) (radius * radius); - if (nextBoolean) { - spawnMungeNE(world, pos, rSqr, radius, spawnData, count); - } else { - spawnMungeSW(world, pos, rSqr, radius, spawnData, count); - } - } - - @Override - public JsonObject getDefaultParameters() { - final JsonObject defParams = new JsonObject(); - defParams.addProperty(Constants.FormatBits.MIN_HEIGHT, 0); - defParams.addProperty(Constants.FormatBits.MAX_HEIGHT, 256); - defParams.addProperty(Constants.FormatBits.VARIATION, 16); - defParams.addProperty(Constants.FormatBits.FREQUENCY, 0.5); - defParams.addProperty(Constants.FormatBits.NODE_SIZE, 8); - return defParams; - } - - @Override - public void setRandom(final Random rand) { - this.random = rand; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/NormalCloudGenerator.java b/src/main/java/com/mcmoddev/orespawn/impl/features/NormalCloudGenerator.java deleted file mode 100644 index bd18dd3..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/NormalCloudGenerator.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.mcmoddev.orespawn.impl.features; - -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public class NormalCloudGenerator extends FeatureBase implements IFeature { - - private NormalCloudGenerator(final Random rand) { - super(rand); - } - - public NormalCloudGenerator() { - this(new Random()); - } - - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos chunkPos) { - final ChunkPos pos = chunkPos; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - mergeDefaults(params, getDefaultParameters()); - - runCache(chunkX, chunkZ, world, spawnData); - - // now to ore spawn - - // lets not offset blind, - final int blockX = chunkX * 16; - final int blockZ = chunkZ * 16; - - final int maxSpread = params.get(Constants.FormatBits.MAX_SPREAD).getAsInt(); - final int medianSize = params.get(Constants.FormatBits.MEDIAN_SIZE).getAsInt(); - final int minHeight = params.get(Constants.FormatBits.MIN_HEIGHT).getAsInt(); - final int maxHeight = params.get(Constants.FormatBits.MAX_HEIGHT).getAsInt(); - final int variance = params.get(Constants.FormatBits.VARIATION).getAsInt(); - int frequency = params.get(Constants.FormatBits.FREQUENCY).getAsInt(); - final int triesMin = params.get(Constants.FormatBits.ATTEMPTS_MIN).getAsInt(); - final int triesMax = params.get(Constants.FormatBits.ATTEMPTS_MAX).getAsInt(); - - // on the X and Z you have a possible 2-chunk range - 32 blocks - subtract the spread to get - // a size that will let us insert by the radius - final int offsetXZ = 32 - maxSpread; - - // you have the distance between minHeight and maxHeight - // this is the actual size of the space - final int sizeY = (maxHeight - minHeight); - final int offsetY = sizeY - maxSpread; - final int radiusXZ = offsetXZ / 2; - - // actual radius for placement is the size minus the spread to center it in the space and - // keep - // from overflowing - final int radiusY = offsetY / 2; - - // we center at the minimum plus the half the height - final int blockY = minHeight + (sizeY / 2); - - final int fSave = frequency; - int tryCount = 0; - - int tries; - - if (triesMax == triesMin) { - tries = triesMax; - } else { - tries = random.nextInt(triesMax - triesMin) + triesMin; - } - - while (tries > 0) { - if (this.random.nextInt(100) <= frequency) { - frequency = fSave; - final int x = blockX + getPoint(0, offsetXZ, radiusXZ) + radiusXZ; - // this should, hopefully, keep us centered between minHeight and maxHeight with - // nothing going above/below those values - final int y = blockY + getPoint(0, offsetY, radiusY); - final int z = blockZ + getPoint(0, offsetXZ, radiusXZ) + radiusXZ; - - int r = medianSize - variance; - - if (variance > 0) { - r += random.nextInt(2 * variance) - variance; - } - - final BlockPos p = new BlockPos(x, y, z); - - if (!spawnCloud(r, maxSpread, minHeight, maxHeight, p, spawnData, world) - && tryCount < 5) { - // make another try! - tries++; - frequency = 100; - tryCount++; - } else { - tryCount = 0; - } - } - - tries--; - } - } - - private boolean spawnCloud(final int size, final int maxSpread, final int minHeight, - final int maxHeight, final BlockPos pos, final ISpawnEntry spawnData, - final World world) { - // spawn one right at the center here, then generate for the cloud and do the math - - if (!spawn(spawnData.getBlocks().getRandomBlock(random), world, pos, - world.provider.getDimension(), true, spawnData)) { - return false; - } - - final int radius = maxSpread / 2; - boolean alreadySpewed = false; - int count = Math.min(size, (int) Math.round(Math.PI * Math.pow(radius, 2))); - - while (count > 0) { - int xp = getPoint(0, maxSpread, radius); - int yp = getPoint(minHeight, maxHeight, (maxHeight - minHeight) / 2); - int zp = getPoint(0, maxSpread, radius); - - BlockPos p = pos.add(xp, yp, zp); - - int z = 0; - - while (z < 5 && !spawn(spawnData.getBlocks().getRandomBlock(random), world, p, - world.provider.getDimension(), true, spawnData)) { - xp = getPoint(0, maxSpread, radius); - yp = getPoint(minHeight, maxHeight, (maxHeight - minHeight) / 2); - zp = getPoint(0, maxSpread, radius); - - p = pos.add(xp, yp, zp); - - z++; - } - - if (z >= 5 && !alreadySpewed) { - OreSpawn.LOGGER.info( - "unable to achieve requested cloud density for cloud centered at %s", pos); - alreadySpewed = true; - } - - count--; - } - - return true; - } - - @Override - public void setRandom(final Random rand) { - this.random = rand; - } - - @Override - public JsonObject getDefaultParameters() { - final JsonObject defParams = new JsonObject(); - defParams.addProperty(Constants.FormatBits.MAX_SPREAD, 16); - defParams.addProperty(Constants.FormatBits.MEDIAN_SIZE, 8); - defParams.addProperty(Constants.FormatBits.MIN_HEIGHT, 8); - defParams.addProperty(Constants.FormatBits.MAX_HEIGHT, 24); - defParams.addProperty(Constants.FormatBits.VARIATION, 4); - defParams.addProperty(Constants.FormatBits.FREQUENCY, 25); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MIN, 4); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MAX, 4); - return defParams; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/PrecisionGenerator.java b/src/main/java/com/mcmoddev/orespawn/impl/features/PrecisionGenerator.java deleted file mode 100644 index 0a88c5c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/PrecisionGenerator.java +++ /dev/null @@ -1,294 +0,0 @@ -package com.mcmoddev.orespawn.impl.features; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants.FormatBits; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public class PrecisionGenerator extends FeatureBase implements IFeature { - - private PrecisionGenerator(final Random rand) { - super(rand); - } - - public PrecisionGenerator() { - this(new Random()); - } - - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos posIn) { - final ChunkPos pos = posIn; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - mergeDefaults(params, getDefaultParameters()); - - runCache(chunkX, chunkZ, world, spawnData); - - // extract parameters - final int nodeCount = params.get(FormatBits.NODE_COUNT).getAsInt(); - final int maxHeight = params.get(FormatBits.MAX_HEIGHT).getAsInt(); - final int minHeight = params.get(FormatBits.MIN_HEIGHT).getAsInt(); - final int nodeSize = params.get(FormatBits.NODE_SIZE).getAsInt(); - - int thisNode = nodeSize; - - // now to use them - for (int c = nodeCount; c > 0; c--) { - int sc; - final HeightRange hr = new HeightRange(minHeight, maxHeight); - final BlockPos spot = chooseSpot(chunkX, chunkZ, hr); - - sc = spawnAtSpot(thisNode, hr, spot, new ChunkPos(chunkX, chunkZ), spawnData, world); - - // bit of feedback - if we underproduce or overproduce a node, the next one gets a - // correction - if (sc != thisNode && sc != 0) { - thisNode += (nodeSize - sc); - } else if (sc == thisNode) { - // if we produced exact size, reset the size - thisNode = nodeSize; - } - - // if we hit a node of size zero or less, we've done something wrong - if (thisNode <= 0) { - thisNode = nodeSize; - } - } - } - - private int spawnAtSpot(final int nodeSize, final HeightRange heightRange, final BlockPos spot, - final ChunkPos pos, final ISpawnEntry spawnData, final World world) { - int spawned = 0; - int c; - - BlockPos act = spot; - int counter = nodeSize; - - while (counter > 0 && spawned < nodeSize) { - c = spawnOreNode(act, pos, spawnData, world, nodeSize, heightRange); - - if (c == 0) { - act = chooseSpot(Math.floorDiv(spot.getX(), 16), Math.floorDiv(spot.getZ(), 16), - heightRange); - } - - counter -= (c + 1); - spawned += c; - } - - return spawned; - } - - private int spawnOreNode(final BlockPos loc, final ChunkPos pos, final ISpawnEntry spawnData, - final World world, final int nodeSize, final HeightRange heightRange) { - - int count = nodeSize; - final int lutType = (nodeSize < 8) ? offsetIndexRef_small.length : offsetIndexRef.length; - final int[] lut = (nodeSize < 8) ? offsetIndexRef_small : offsetIndexRef; - final Vec3i[] offs = new Vec3i[lutType]; - - System.arraycopy((nodeSize < 8) ? offsets_small : offsets, 0, offs, 0, lutType); - - if (nodeSize < 27) { - final int[] scrambledLUT = new int[lutType]; - System.arraycopy(lut, 0, scrambledLUT, 0, scrambledLUT.length); - scramble(scrambledLUT, this.random); - - int nc = 0; - - for (; count > 0 && nc <= nodeSize; count--) { - final IBlockState oreBlock = spawnData.getBlocks().getRandomBlock(this.random); - final Vec3i offset = offs[scrambledLUT[--count]]; - final BlockPos p = fixMungeOffset(offset, loc, heightRange, pos); - final int dimension = world.provider.getDimension(); - - if (spawn(oreBlock, world, p, dimension, true, spawnData)) { - nc++; - } - } - - return nc; - } - - return spawnFill(spawnData, world, pos, loc, nodeSize, heightRange); - } - - private BlockPos fixMungeOffset(final Vec3i offset, final BlockPos spot, - final HeightRange heightRange, final ChunkPos pos) { - final BlockPos p = spot.add(offset); - final ChunkPos x1z1 = new ChunkPos(pos.x + 1, pos.z + 1); - final int xMax = x1z1.getXEnd(); - final int zMax = x1z1.getZEnd(); - final int xMin = pos.getXStart(); - final int zMin = pos.getZStart(); - - int xmod = offset.getX(); - int ymod = offset.getY(); - int zmod = offset.getZ(); - - // correct the points values to not cause the Y coordinate to go outside the permissable - // range - if (p.getY() < heightRange.getMin() || p.getY() > heightRange.getMax()) { - ymod = rescaleOffset(ymod, spot.getY(), heightRange.getMin(), heightRange.getMax()); - } - - if (p.getX() < xMin || p.getX() > xMax) { - xmod = rescaleOffset(xmod, spot.getX(), xMin, xMax); - } - - if (p.getZ() < zMin || p.getZ() > zMax) { - zmod = rescaleOffset(zmod, spot.getZ(), zMin, zMax); - } - - return spot.add(xmod, ymod, zmod); - } - - private int rescaleOffset(final int offsetIn, final int centerIn, final int minimumIn, - final int maximumIn) { - final int actual = centerIn + offsetIn; - int wrapDistance; - final int range = maximumIn - minimumIn; - int workingPoint; - - if (actual < minimumIn) { - wrapDistance = minimumIn - actual; - } else { - wrapDistance = actual - maximumIn; - } - - if (wrapDistance < 0) { - wrapDistance = ((-1) * wrapDistance) % range; - } else { - wrapDistance %= range; - } - - if (actual < minimumIn) { - workingPoint = maximumIn - wrapDistance; - } else { - workingPoint = minimumIn + wrapDistance; - } - - return workingPoint - centerIn; - } - - private int spawnFill(final ISpawnEntry spawnData, final World world, final ChunkPos pos, - final BlockPos loc, final int nodeSize, final HeightRange heightRange) { - - final double radius = Math.pow(nodeSize, 1.0 / 3.0) * (3.0 / 4.0 / Math.PI) + 2; - final int rSqr = (int) Math.ceil(radius * radius); - - return spawnPrecise(spawnData, world, pos, loc, heightRange, !this.random.nextBoolean(), radius, rSqr, - nodeSize); - } - - private int spawnPrecise(final ISpawnEntry spawnData, final World world, final ChunkPos pos, - final BlockPos loc, final HeightRange heightRange, final boolean toPositive, - final double radius, final int rSqr, final int nodeSize) { - int quantity = nodeSize; - int nc = 0; - - for (int dy = (int) (-1 * radius); dy < radius; dy++) { - for (int dx = getStart(toPositive, radius); endCheck(toPositive, dx, - radius); dx = countItem(dx, toPositive)) { - for (int dz = getStart(toPositive, radius); endCheck(toPositive, dz, - radius); dz = countItem(dz, toPositive)) { - if (doCheckSpawn(dx, dy, dz, rSqr, heightRange, spawnData, world, pos, - loc) >= 0) { - nc++; - quantity--; - - if (nc >= nodeSize || quantity <= 0) { - return nc; - } - } - } - } - } - - return nc; - } - - private int doCheckSpawn(final int dx, final int dy, final int dz, final int rSqr, - final HeightRange heightRange, final ISpawnEntry spawnData, final World world, - final ChunkPos pos, final BlockPos loc) { - if (getABC(dx, dy, dz) <= rSqr) { - final BlockPos p = fixMungeOffset(new Vec3i(dx, dy, dz), loc, heightRange, pos); - final IBlockState bl = spawnData.getBlocks().getRandomBlock(this.random); - if (bl.getBlock().equals(net.minecraft.init.Blocks.AIR)) return -1; - return spawn(bl, world, p, world.provider.getDimension(), true, spawnData) ? 1 : 0; - } - - return -1; - } - - private int getPoint(final int lowerBound, final int upperBound) { - final List arr = new ArrayList<>(); - - for (int i = lowerBound; i <= upperBound; i++) { - arr.add(i); - } - - return arr.get(this.random.nextInt(arr.size())); - } - - private BlockPos chooseSpot(final int xPosition, final int zPosition, - final HeightRange heightRange) { - final int xRet = getPoint(0, 15) + (xPosition * 16); - final int zRet = getPoint(0, 15) + (zPosition * 16); - final int yRet = getPoint(heightRange.getMin(), heightRange.getMax()); - - return new BlockPos(xRet, yRet, zRet); - } - - @Override - public void setRandom(final Random rand) { - this.random = rand; - } - - @Override - public JsonObject getDefaultParameters() { - final JsonObject defaults = new JsonObject(); - defaults.addProperty(FormatBits.NODE_COUNT, 4); - defaults.addProperty(FormatBits.MIN_HEIGHT, 16); - defaults.addProperty(FormatBits.MAX_HEIGHT, 80); - defaults.addProperty(FormatBits.NODE_SIZE, 8); - return defaults; - } - - private class HeightRange { - - private int min; - private int max; - - HeightRange(final int min, final int max) { - this.min = min; - this.max = max; - } - - int getMin() { - return this.min; - } - - int getMax() { - return this.max; - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/UnderFluid.java b/src/main/java/com/mcmoddev/orespawn/impl/features/UnderFluid.java deleted file mode 100644 index f1c4697..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/UnderFluid.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * - */ -package com.mcmoddev.orespawn.impl.features; - -import java.util.Random; -import java.util.Set; -import java.util.HashSet; -import java.util.List; -import java.util.stream.StreamSupport; -import java.util.stream.Collectors; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; - -/** - * @author Daniel Hazelton - * - */ -public class UnderFluid extends FeatureBase implements IFeature { - - /** - * @param rand - */ - public UnderFluid() { - super(new Random()); - } - - /** - * Try to generate this feature to the specified parameters in the specified chunk. - *
- *

Theory: - *

For up to 2*maximum_tries pick a random spot in the spawn zone do: - *

Check the 5x5x5 region around that spot for the fluid: - *

if found, store the BlockPos of the fluid, if this store has reached maximum_tries entries, stop iteration - *

For each of the found positions, if any, seek up and down from the position to find the lower bound of the fluid - *

At the fluids lower bound, generate the node. - *

- * - * @param world {@link net.minecraft.world.World} World this chunk is in - * @param chunkGenerator {@link net.minecraft.world.gen.IChunkGenerator} Chunk generator for this chunk - * @param chunkProvider {@link net.minecraft.world.chunk.IChunkGenerator} Chunk provider for this chunk - * @param spawnData {@link com.mcmoddev.orespawn.api.os3.ISpawnEntry} Parameters and data on this spawn from the configuration file - * @param chunkPos {@link net.minecraft.util.math.ChunkPos} Absolute in-world coordinates, on the chunk grid, for this chunk (can get lowest value X/Z block coordinates for this chunk by multiplying provided X/Z by 16) - * @see com.mcmoddev.orespawn.api.IFeature#generate(net.minecraft.world.World, net.minecraft.world.gen.IChunkGenerator, net.minecraft.world.chunk.IChunkProvider, com.mcmoddev.orespawn.api.os3.ISpawnEntry, net.minecraft.util.math.ChunkPos) - */ - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos chunkPos) { - final ChunkPos pos = chunkPos; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - mergeDefaults(params, getDefaultParameters()); - - runCache(chunkX, chunkZ, world, spawnData); - - // now to ore spawn - - final int blockX = chunkX * 16 + 8; - final int blockZ = chunkZ * 16 + 8; - - - final int minHeight = params.get(Constants.FormatBits.MIN_HEIGHT).getAsInt(); - final int maxHeight = params.get(Constants.FormatBits.MAX_HEIGHT).getAsInt(); - final int variance = params.get(Constants.FormatBits.VARIATION).getAsInt(); - final int triesMin = params.get(Constants.FormatBits.ATTEMPTS_MIN).getAsInt(); - final int triesMax = params.get(Constants.FormatBits.ATTEMPTS_MAX).getAsInt(); - final int nodeSize = params.get(Constants.FormatBits.NODE_SIZE).getAsInt(); - final Fluid fluid = FluidRegistry.getFluid(params.get(Constants.FormatBits.FLUID).getAsString()); - - int tries = this.random.nextInt(triesMax - triesMin + 1) + triesMin; - int ySpan = maxHeight - minHeight; - int surveySize = ((256 * (16 * ySpan)) / 4) / 125; - BlockPos refBlock = new BlockPos(blockX, minHeight, blockZ); - Block fluidBlock = fluid.getBlock(); - - Set found = new HashSet<>(); - - int surveyCount = 0; - int spawnCount = 0; - - while(surveyCount < surveySize && spawnCount < tries) { - BlockPos sampleCenter = refBlock.add(this.random.nextInt(16), this.random.nextInt(ySpan), this.random.nextInt(16)); - BlockPos lowSide = sampleCenter.add(-2,-2,-2); - BlockPos highSide = sampleCenter.add(2,2,2); - Chunk chunk = world.getChunk(sampleCenter); - - if(!found.contains(sampleCenter)) { - found.addAll(findPossibleTargets(chunk, lowSide, highSide, fluidBlock, minHeight)); - - if(!found.isEmpty()) { - BlockPos target = found.toArray(new BlockPos[0])[this.random.nextInt(found.size())]; - spawnCount += maybeSpawn(target, minHeight, maxHeight, nodeSize, variance, world, spawnData); - } - surveyCount++; - } - } - } - - private int maybeSpawn(BlockPos target, int minHeight, int maxHeight, int nodeSize, - int variance, World world, ISpawnEntry spawnData) { - BlockPos mp = target; - while(world.getBlockState(mp).getMaterial().isLiquid() && mp.getY() >= minHeight) { - mp = mp.down(); - } - - if (mp.getY() > minHeight && mp.getY() < maxHeight) { - // calculate actual size for this node - int size = this.random.nextInt(nodeSize - variance + 1) + this.random.nextInt(variance); - // try to spawn now, as we do ***NOT*** want the node to wrap, we have to do this different... - // so... we copy vanilla spawn logic, to a degree, I think - spawnOre(world, spawnData, mp, size); - return 1; - } - - - return 0; - } - - private List findPossibleTargets(Chunk chunk, BlockPos lowSide, BlockPos highSide, - Block fluidBlock, int minHeight) { - return StreamSupport.stream(BlockPos.getAllInBoxMutable(lowSide, highSide).spliterator(), false) - .filter( bp -> chunk.getBlockState(bp).getMaterial().isLiquid() && - chunk.getBlockState(bp).getBlock().equals(fluidBlock) && - bp.getY() >= minHeight ) - .map(BlockPos.MutableBlockPos::toImmutable) - .collect(Collectors.toList()); - } - - private void spawnOre(final World world, final ISpawnEntry spawnData, final BlockPos pos, - final int quantity) { - int count = quantity; - final int lutType = (quantity < 8) ? offsetIndexRef_small.length : offsetIndexRef.length; - final int[] lut = (quantity < 8) ? offsetIndexRef_small : offsetIndexRef; - final Vec3i[] offs = new Vec3i[lutType]; - - System.arraycopy((quantity < 8) ? offsets_small : offsets, 0, offs, 0, lutType); - - if (quantity < 27) { - final int[] scrambledLUT = new int[lutType]; - System.arraycopy(lut, 0, scrambledLUT, 0, scrambledLUT.length); - scramble(scrambledLUT, this.random); - - while (count > 0) { - final IBlockState oreBlock = spawnData.getBlocks().getRandomBlock(random); - if (oreBlock.getBlock().equals(net.minecraft.init.Blocks.AIR)) return; - final BlockPos target = pos.add(offs[scrambledLUT[--count]]); - spawn(oreBlock, world, target, world.provider.getDimension(), true, spawnData); - } - - return; - } - - doSpawnFill(this.random.nextBoolean(), count, world, spawnData, pos); - } - - private void doSpawnFill(final boolean nextBoolean, final int quantity, final World world, - final ISpawnEntry spawnData, final BlockPos pos) { - final int count = quantity; - final double radius = Math.pow(quantity, 1.0 / 3.0) * (3.0 / 4.0 / Math.PI) + 2; - final int rSqr = (int) (radius * radius); - if (nextBoolean) { - spawnMungeNE(world, pos, rSqr, radius, spawnData, count); - } else { - spawnMungeSW(world, pos, rSqr, radius, spawnData, count); - } - } - - /* (non-Javadoc) - * @see com.mcmoddev.orespawn.api.IFeature#setRandom(java.util.Random) - */ - @Override - public void setRandom(Random rand) { - this.random = rand; - } - - /* (non-Javadoc) - * @see com.mcmoddev.orespawn.api.IFeature#getDefaultParameters() - */ - @Override - public JsonObject getDefaultParameters() { - final JsonObject defParams = new JsonObject(); - defParams.addProperty(Constants.FormatBits.MIN_HEIGHT, 0); - defParams.addProperty(Constants.FormatBits.MAX_HEIGHT, 256); - defParams.addProperty(Constants.FormatBits.VARIATION, 16); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MIN, 4); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MAX, 4); - defParams.addProperty(Constants.FormatBits.NODE_SIZE, 8); - defParams.addProperty(Constants.FormatBits.FLUID, "water"); - return defParams; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/features/VeinGenerator.java b/src/main/java/com/mcmoddev/orespawn/impl/features/VeinGenerator.java deleted file mode 100644 index dd3288c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/features/VeinGenerator.java +++ /dev/null @@ -1,583 +0,0 @@ -package com.mcmoddev.orespawn.impl.features; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -import com.google.common.collect.Lists; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.FeatureBase; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.WeightedRandom; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; - -public class VeinGenerator extends FeatureBase implements IFeature { - - private VeinGenerator(final Random rand) { - super(rand); - } - - public VeinGenerator() { - this(new Random()); - } - - @Override - public void generate(final World world, final IChunkGenerator chunkGenerator, - final IChunkProvider chunkProvider, final ISpawnEntry spawnData, final ChunkPos _pos) { - final ChunkPos pos = _pos; - final JsonObject params = spawnData.getFeature().getFeatureParameters(); - - // First, load cached blocks for neighboring chunk ore spawns - final int chunkX = pos.x; - final int chunkZ = pos.z; - - runCache(chunkX, chunkZ, world, spawnData); - mergeDefaults(params, getDefaultParameters()); - - // now to ore spawn - - final int blockX = chunkX * 16 + 8; - final int blockZ = chunkZ * 16 + 8; - - final int minY = params.get(Constants.FormatBits.MIN_HEIGHT).getAsInt(); - final int maxY = params.get(Constants.FormatBits.MAX_HEIGHT).getAsInt(); - final int vari = params.get(Constants.FormatBits.VARIATION).getAsInt(); - final int freq = params.get(Constants.FormatBits.FREQUENCY).getAsInt(); - final int length = params.get(Constants.FormatBits.LENGTH).getAsInt(); - final EnumFacing startingFace = getFaceFromString( - params.get(Constants.FormatBits.STARTINGFACE).getAsString()); - final int nodeSize = params.get(Constants.FormatBits.NODE_SIZE).getAsInt(); - final int triesMin = params.get(Constants.FormatBits.ATTEMPTS_MIN).getAsInt(); - final int triesMax = params.get(Constants.FormatBits.ATTEMPTS_MAX).getAsInt(); - - int tries; - - if (triesMax == triesMin) { - tries = triesMax; - } else { - tries = random.nextInt(triesMax - triesMin) + triesMin; - } - - // we have an offset into the chunk but actually need something more - while (tries > 0) { - if (this.random.nextInt(100) <= freq) { - final int x = blockX + random.nextInt(16); - final int y = random.nextInt(maxY - minY) + minY; - final int z = blockZ + random.nextInt(16); - - final int r; - - if (vari > 0) { - r = random.nextInt(2 * vari) - vari; - } else { - r = 0; - } - - spawnVein(length + r, nodeSize, startingFace, new BlockPos(x, y, z), spawnData, - world); - } - - tries--; - } - } - - private EnumFacing getFaceFromString(final String direction) { - final String work = direction.toLowerCase(); - switch (work) { - case "north": - return EnumFacing.NORTH; - case "south": - return EnumFacing.SOUTH; - case "east": - return EnumFacing.EAST; - case "west": - return EnumFacing.WEST; - case "down": - return EnumFacing.DOWN; - case "up": - return EnumFacing.UP; - case "random": - return EnumFacing.VALUES[this.random.nextInt(EnumFacing.VALUES.length)]; - case "vertical": - return this.random.nextBoolean() ? EnumFacing.UP : EnumFacing.DOWN; - case "horizontal": - return EnumFacing.HORIZONTALS[this.random.nextInt(EnumFacing.HORIZONTALS.length)]; - default: - OreSpawn.LOGGER.error( - "Invalid value %s found in parameters for vein spawn, returning \"north\"", - direction); - return EnumFacing.NORTH; - } - } - - private enum EnumSquare { - TOP_EDGE(0), LEFT_EDGE(1), BOTTOM_EDGE(2), RIGHT_EDGE(3), LEFT_TOP(4), LEFT_BOTTOM(5), - RIGHT_TOP(6), RIGHT_BOTTOM(7), FACE(8); - - private int index; - - private EnumSquare(int index) { - this.index = index; - } - - final public int getIndex() { - return this.index; - } - } - - private void spawnVein(final int veinLength, final int nodeSize, final EnumFacing startingFace, - final BlockPos blockPos, final ISpawnEntry spawnData, final World world) { - EnumFacing face = startingFace; - EnumSquare square = EnumSquare.values()[this.random.nextInt(EnumSquare.values().length)]; - BlockPos workingPos = new BlockPos(blockPos); - - if (!spawnData.getMatcher().test(world.getBlockState(blockPos))) { - return; - } - - // build vein - final List points = Lists.newLinkedList(); - for (; points.size() < veinLength;) { - points.add(workingPos); - List nextFaces = getNextFaceSet(square, face); - - if (!nextFaces.isEmpty()) { - BlockPos temp = workingPos; - for (EnumFacing f : nextFaces) { - temp = temp.offset(f, 1); - } - nextFaces.clear(); - points.add(temp); - workingPos = temp; - } - - face = getNextStartingFace(square, face); - square = getNextSquare(); - } - - spawnOre(world, spawnData, blockPos, nodeSize); - for (final BlockPos pos : points) { - spawnOre(world, spawnData, pos, nodeSize); - } - } - - private class SquareWeight extends WeightedRandom.Item { - - private EnumSquare item; - - public SquareWeight(EnumSquare item, int weight) { - super(weight); - this.item = item; - } - } - - private EnumSquare getNextSquare() { - float[] weights = new float[] { - 0.12f, 0.12f, 0.12f, 0.12f, 0.0475f, 0.0475f, 0.0475f, 0.0475f, 0.33f - }; - List items = new ArrayList<>(); - - for (EnumSquare sq : EnumSquare.values()) { - items.add(new SquareWeight(sq, (int) (weights[sq.getIndex()] * 10000))); - } - - return ((SquareWeight) WeightedRandom.getRandomItem(this.random, items)).item; - } - - private static final EnumFacing[][][] congruentSquares = new EnumFacing[][][] { - // Index 0 - DOWN - new EnumFacing[][] { - new EnumFacing[] { - EnumFacing.SOUTH, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.WEST, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.NORTH, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.EAST, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.WEST - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.NORTH, EnumFacing.WEST - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.EAST - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.EAST - } // RIGHT_TOP - }, new EnumFacing[][] { - // Index 1 - UP - new EnumFacing[] { - EnumFacing.SOUTH, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.WEST, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.NORTH, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.EAST, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.WEST - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.NORTH, EnumFacing.WEST - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.EAST - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.SOUTH, EnumFacing.EAST - } // RIGHT_TOP - }, new EnumFacing[][] { - // Index 2 - NORTH - new EnumFacing[] { - EnumFacing.UP, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.WEST, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.DOWN, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.EAST, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.UP, EnumFacing.WEST - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.WEST - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.EAST - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.UP, EnumFacing.EAST - } // RIGHT_TOP - }, new EnumFacing[][] { - // Index 3 - SOUTH - new EnumFacing[] { - EnumFacing.UP, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.EAST, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.DOWN, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.WEST, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.UP, EnumFacing.EAST - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.EAST - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.WEST - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.UP, EnumFacing.WEST - } // RIGHT_TOP - }, new EnumFacing[][] { - // Index 4 - WEST - new EnumFacing[] { - EnumFacing.UP, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.SOUTH, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.DOWN, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.NORTH, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.UP, EnumFacing.SOUTH - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.SOUTH - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.NORTH - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.UP, EnumFacing.NORTH - } // RIGHT_TOP - }, new EnumFacing[][] { - // Index 5 - EAST - new EnumFacing[] { - EnumFacing.UP, null - }, // TOP_EDGE - new EnumFacing[] { - EnumFacing.NORTH, null - }, // LEFT_EDGE - new EnumFacing[] { - EnumFacing.DOWN, null - }, // BOTTOM_EDGE - new EnumFacing[] { - EnumFacing.SOUTH, null - }, // RIGHT_EDGE - new EnumFacing[] { - EnumFacing.UP, EnumFacing.NORTH - }, // LEFT_TOP - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.NORTH - }, // LEFT_BOTTOM - new EnumFacing[] { - EnumFacing.DOWN, EnumFacing.SOUTH - }, // RIGHT_BOTTOM - new EnumFacing[] { - EnumFacing.UP, EnumFacing.SOUTH - } // RIGHT_TOP - } - }; - - private EnumFacing getNextStartingFace(EnumSquare square, EnumFacing face) { - - if ((this.random.nextBoolean()) || (square == EnumSquare.FACE)) { - return face; - } - - EnumFacing[] possibles = Arrays.asList(congruentSquares[face.getIndex()][square.getIndex()]) - .stream().filter(it -> it != null).toArray(EnumFacing[]::new); - - if (possibles.length > 1) { - return possibles[this.random.nextInt(possibles.length)]; - } - return possibles[0]; - } - - private List getNextFaceSet(EnumSquare square, EnumFacing face) { - List rv = new ArrayList<>(); - - rv.add(face); - - switch (square) { - case BOTTOM_EDGE: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(EnumFacing.DOWN); - break; - case UP: - case DOWN: - rv.add(EnumFacing.NORTH); - break; - default: - break; - } - break; - case TOP_EDGE: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(EnumFacing.UP); - break; - case UP: - case DOWN: - rv.add(EnumFacing.SOUTH); - break; - default: - break; - } - break; - case LEFT_EDGE: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateYCCW()); - break; - case UP: - case DOWN: - rv.add(EnumFacing.EAST); - break; - default: - break; - } - break; - case RIGHT_EDGE: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateY()); - break; - case UP: - case DOWN: - rv.add(EnumFacing.WEST); - break; - default: - break; - } - break; - case FACE: - break; - case LEFT_TOP: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateYCCW()); - rv.add(EnumFacing.UP); - break; - case UP: - case DOWN: - rv.add(EnumFacing.EAST); - rv.add(EnumFacing.SOUTH); - break; - default: - break; - } - break; - case LEFT_BOTTOM: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateYCCW()); - rv.add(EnumFacing.DOWN); - break; - case UP: - case DOWN: - rv.add(EnumFacing.EAST); - rv.add(EnumFacing.NORTH); - break; - default: - break; - } - break; - case RIGHT_TOP: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateY()); - rv.add(EnumFacing.UP); - break; - case UP: - case DOWN: - rv.add(EnumFacing.EAST); - rv.add(EnumFacing.SOUTH); - break; - default: - break; - } - break; - case RIGHT_BOTTOM: - switch (face) { - case EAST: - case WEST: - case NORTH: - case SOUTH: - rv.add(face.rotateY()); - rv.add(EnumFacing.DOWN); - break; - case UP: - case DOWN: - rv.add(EnumFacing.EAST); - rv.add(EnumFacing.NORTH); - break; - default: - break; - } - break; - default: - break; - } - - return rv; - } - - private void spawnOre(final World world, final ISpawnEntry spawnData, final BlockPos pos, - final int quantity) { - int count = quantity; - final int lutType = (quantity < 8) ? offsetIndexRef_small.length : offsetIndexRef.length; - final int[] lut = (quantity < 8) ? offsetIndexRef_small : offsetIndexRef; - final Vec3i[] offs = new Vec3i[lutType]; - - System.arraycopy((quantity < 8) ? offsets_small : offsets, 0, offs, 0, lutType); - - if (quantity < 27) { - final int[] scrambledLUT = new int[lutType]; - System.arraycopy(lut, 0, scrambledLUT, 0, scrambledLUT.length); - scramble(scrambledLUT, this.random); - - while (count > 0) { - final IBlockState oreBlock = spawnData.getBlocks().getRandomBlock(random); - if (oreBlock.getBlock().equals(net.minecraft.init.Blocks.AIR)) return; - final BlockPos target = pos.add(offs[scrambledLUT[--count]]); - spawn(oreBlock, world, target, world.provider.getDimension(), true, spawnData); - } - - return; - } - - doSpawnFill(this.random.nextBoolean(), count, world, spawnData, pos); - } - - private void doSpawnFill(final boolean nextBoolean, final int quantity, final World world, - final ISpawnEntry spawnData, final BlockPos pos) { - final int count = quantity; - final double radius = Math.pow(quantity, 1.0 / 3.0) * (3.0 / 4.0 / Math.PI) + 2; - final int rSqr = (int) (radius * radius); - if (nextBoolean) { - spawnMungeNE(world, pos, rSqr, radius, spawnData, count); - } else { - spawnMungeSW(world, pos, rSqr, radius, spawnData, count); - } - } - - @Override - public JsonObject getDefaultParameters() { - final JsonObject defParams = new JsonObject(); - defParams.addProperty(Constants.FormatBits.MIN_HEIGHT, 0); - defParams.addProperty(Constants.FormatBits.MAX_HEIGHT, 256); - defParams.addProperty(Constants.FormatBits.VARIATION, 16); - defParams.addProperty(Constants.FormatBits.FREQUENCY, 50); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MAX, 8); - defParams.addProperty(Constants.FormatBits.ATTEMPTS_MIN, 4); - defParams.addProperty(Constants.FormatBits.LENGTH, 16); - defParams.addProperty(Constants.FormatBits.STARTINGFACE, "north"); - defParams.addProperty(Constants.FormatBits.NODE_SIZE, 3); - return defParams; - } - - @Override - public void setRandom(final Random rand) { - this.random = rand; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationAcceptAny.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationAcceptAny.java deleted file mode 100644 index 057eacc..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationAcceptAny.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.data.Constants.ConfigNames; - -import net.minecraft.world.biome.Biome; - -public class BiomeLocationAcceptAny implements BiomeLocation { - - @Override - public boolean matches(final Biome biome) { - return true; - } - - @Override - public JsonElement serialize() { - final JsonObject rv = new JsonObject(); - rv.add(ConfigNames.BLACKLIST, new JsonArray()); - return rv; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationComposition.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationComposition.java deleted file mode 100644 index 4abce92..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationComposition.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.world.biome.Biome; - -public final class BiomeLocationComposition implements BiomeLocation { - - private final BiomeLocation inclusions; - - private final BiomeLocation exclusions; - - private final int hash; - - public BiomeLocationComposition(final BiomeLocation inclusions, - final BiomeLocation exclusions) { - this.inclusions = inclusions; - this.exclusions = exclusions; - this.hash = Objects.hash(inclusions, exclusions); - } - - @Override - public boolean matches(final Biome biome) { - final boolean inWhite = this.inclusions.matches(biome); - final boolean inBlack = this.exclusions.matches(biome); - - return !inBlack && inWhite; - } - - @Override - public int hashCode() { - return this.hash; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof BiomeLocationComposition) { - final BiomeLocationComposition other = (BiomeLocationComposition) obj; - return this.inclusions.equals(other.inclusions) - && this.exclusions.equals(other.exclusions); - } - - return false; - } - - @Override - public ImmutableList getBiomes() { - final List temp = new LinkedList<>(); - temp.addAll(this.inclusions.getBiomes()); - temp.addAll(this.exclusions.getBiomes()); - return ImmutableList.copyOf(temp); - } - - public BiomeLocation getInclusions() { - return this.inclusions; - } - - public BiomeLocation getExclusions() { - return this.exclusions; - } - - @Override - public JsonElement serialize() { - final JsonObject rv = new JsonObject(); - - rv.add(Constants.ConfigNames.BLACKLIST, this.exclusions.serialize()); - if (!(this.inclusions instanceof BiomeLocationEmpty)) { - rv.add(Constants.ConfigNames.WHITELIST, this.inclusions.serialize()); - } - - return rv; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationDictionary.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationDictionary.java deleted file mode 100644 index d45d846..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationDictionary.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.mcmoddev.orespawn.api.BiomeLocation; - -import net.minecraft.world.biome.Biome; -import net.minecraftforge.common.BiomeDictionary; - -public final class BiomeLocationDictionary implements BiomeLocation { - - private final BiomeDictionary.Type type; - - private final int hash; - - public BiomeLocationDictionary(final BiomeDictionary.Type type) { - this.type = type; - this.hash = type.hashCode(); - } - - @Override - public boolean matches(final Biome biome) { - return BiomeDictionary.hasType(biome, this.type); - } - - @Override - public int hashCode() { - return this.hash; - } - - @Override - public boolean equals(final Object obj) { - return (obj == this) || ((obj instanceof BiomeLocationDictionary) - && this.type.equals(((BiomeLocationDictionary) obj).type)); - } - - public BiomeDictionary.Type getType() { - return this.type; - } - - @Override - public ImmutableList getBiomes() { - return ImmutableList.copyOf(BiomeDictionary.getBiomes(this.type)); - } - - @Override - public JsonElement serialize() { - return new JsonPrimitive(this.type.toString().toUpperCase()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationEmpty.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationEmpty.java deleted file mode 100644 index 44db55a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationEmpty.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.mcmoddev.orespawn.api.BiomeLocation; - -import net.minecraft.world.biome.Biome; - -public class BiomeLocationEmpty implements BiomeLocation { - - @Override - public boolean matches(final Biome biome) { - return false; - } - - @Override - public JsonElement serialize() { - return new JsonArray(); - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationList.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationList.java deleted file mode 100644 index 56b3408..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationList.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import java.util.LinkedList; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.mcmoddev.orespawn.api.BiomeLocation; - -import net.minecraft.world.biome.Biome; - -public final class BiomeLocationList implements BiomeLocation { - - private final ImmutableSet locations; - - private final int hash; - - public BiomeLocationList(final ImmutableSet locations) { - this.locations = locations; - this.hash = locations.hashCode(); - } - - @Override - public boolean matches(final Biome biome) { - return this.locations.stream().anyMatch(loc -> loc.matches(biome)); - } - - @Override - public int hashCode() { - return this.hash; - } - - @Override - public boolean equals(final Object obj) { - return (obj == this) || ((obj instanceof BiomeLocationList) - && this.locations.equals(((BiomeLocationList) obj).locations)); - } - - @Override - public ImmutableList getBiomes() { - final List temp = new LinkedList<>(); - locations.stream().forEach(bl -> temp.addAll(bl.getBiomes())); - return ImmutableList.copyOf(temp); - } - - public ImmutableSet getLocations() { - return this.locations; - } - - @Override - public JsonElement serialize() { - final JsonArray rv = new JsonArray(); - this.locations.stream().filter(bl -> (!(bl instanceof BiomeLocationEmpty))) - .forEach(bl -> rv.add(bl.serialize())); - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationSingle.java b/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationSingle.java deleted file mode 100644 index 0b862c7..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/location/BiomeLocationSingle.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.mcmoddev.orespawn.impl.location; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.mcmoddev.orespawn.api.BiomeLocation; - -import net.minecraft.world.biome.Biome; - -public final class BiomeLocationSingle implements BiomeLocation { - - private final Biome biome; - - private final int hash; - - public BiomeLocationSingle(final Biome biome) { - this.biome = biome; - this.hash = biome.hashCode(); - } - - @Override - public boolean matches(final Biome biome) { - return this.biome.equals(biome); - } - - @Override - public ImmutableList getBiomes() { - return ImmutableList.of(this.biome); - } - - @Override - public int hashCode() { - return this.hash; - } - - @Override - public boolean equals(final Object obj) { - return (obj == this) || ((obj instanceof BiomeLocationSingle) - && this.biome.equals(((BiomeLocationSingle) obj).biome)); - } - - public Biome getBiome() { - return this.biome; - } - - @Override - public JsonElement serialize() { - return new JsonPrimitive(this.biome.getRegistryName().toString()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/BiomeBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/BiomeBuilder.java deleted file mode 100644 index a8a9bf5..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/BiomeBuilder.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableSet; -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.api.os3.IBiomeBuilder; -import com.mcmoddev.orespawn.impl.location.BiomeLocationAcceptAny; -import com.mcmoddev.orespawn.impl.location.BiomeLocationComposition; -import com.mcmoddev.orespawn.impl.location.BiomeLocationEmpty; -import com.mcmoddev.orespawn.impl.location.BiomeLocationList; -import com.mcmoddev.orespawn.impl.location.BiomeLocationSingle; - -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.biome.Biome; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class BiomeBuilder implements IBiomeBuilder { - - private final List whitelist = new LinkedList<>(); - private final List blacklist = new LinkedList<>(); - - private boolean acceptAll = false; - - public BiomeBuilder() { - // - } - - @Override - public IBiomeBuilder addWhitelistEntry(final Biome biome) { - this.whitelist.add(biome); - return this; - } - - @Override - public IBiomeBuilder addWhitelistEntry(final String biomeName) { - return this.addWhitelistEntry(new ResourceLocation(biomeName)); - } - - @Override - public IBiomeBuilder addWhitelistEntry(final ResourceLocation biomeResourceLocation) { - return this.addWhitelistEntry(ForgeRegistries.BIOMES.getValue(biomeResourceLocation)); - } - - @Override - public IBiomeBuilder addBlacklistEntry(final Biome biome) { - this.blacklist.add(biome); - return this; - } - - @Override - public IBiomeBuilder addBlacklistEntry(final String biomeName) { - return this.addBlacklistEntry(new ResourceLocation(biomeName)); - } - - @Override - public IBiomeBuilder addBlacklistEntry(final ResourceLocation biomeResourceLocation) { - return this.addBlacklistEntry(ForgeRegistries.BIOMES.getValue(biomeResourceLocation)); - } - - @Override - public IBiomeBuilder setAcceptAll() { - this.acceptAll = true; - return this; - } - - @Override - public BiomeLocation create() { - if (this.acceptAll) { - return new BiomeLocationAcceptAny(); - } - - BiomeLocation whitelistI; - BiomeLocation blacklistI; - if (this.whitelist.isEmpty()) { - if (!this.blacklist.isEmpty()) { - whitelistI = new BiomeLocationAcceptAny(); - } else { - whitelistI = new BiomeLocationEmpty(); - } - } else { - whitelistI = new BiomeLocationList(ImmutableSet.copyOf( - this.whitelist.stream().map(biome -> new BiomeLocationSingle(biome)) - .collect(Collectors.toList()))); - } - - if (this.blacklist.isEmpty()) { - blacklistI = new BiomeLocationEmpty(); - } else { - blacklistI = new BiomeLocationList(ImmutableSet.copyOf( - this.blacklist.stream().map(biome -> new BiomeLocationSingle(biome)) - .collect(Collectors.toList()))); - } - - return new BiomeLocationComposition(whitelistI, blacklistI); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockBuilder.java deleted file mode 100644 index 7bc3f62..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockBuilder.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.exceptions.BadStateValueException; -import com.mcmoddev.orespawn.api.os3.IBlockBuilder; -import com.mcmoddev.orespawn.api.os3.IBlockDefinition; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class BlockBuilder implements IBlockBuilder { - - private IBlockState blockState; - private int chance; - private boolean isValid = true; - - public BlockBuilder() { - // nothing to do here - } - - @Override - public IBlockBuilder setFromBlockState(final IBlockState blockState) { - final ResourceLocation key = blockState.getBlock().getRegistryName(); - if (!ForgeRegistries.BLOCKS.containsKey(key)) { - this.isValid = false; - } - return this.setFromBlockStateWithChance(blockState, 100); - } - - @Override - public IBlockBuilder setFromBlock(final Block block) { - final ResourceLocation key = block.getRegistryName(); - if (!ForgeRegistries.BLOCKS.containsKey(key)) { - this.isValid = false; - } - return this.setFromBlockState(block.getDefaultState()); - } - - @Override - public IBlockBuilder setFromName(final String blockName) { - return this.setFromName(new ResourceLocation(blockName)); - } - - @Override - public IBlockBuilder setFromName(final String blockName, final String state) { - return this.setFromName(new ResourceLocation(blockName), state); - } - - @Override - public IBlockBuilder setFromName(final String blockName, final int metadata) { - return this.setFromName(new ResourceLocation(blockName), metadata); - } - - @Override - public IBlockBuilder setFromName(final ResourceLocation blockResourceLocation) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - return this.setFromBlock(ForgeRegistries.BLOCKS.getValue(blockResourceLocation)); - } - - @Override - public IBlockBuilder setFromName(final ResourceLocation blockResourceLocation, - final String state) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - final Block tempBlock = ForgeRegistries.BLOCKS.getValue(blockResourceLocation); - try { - return this.setFromBlockState(StateUtil.deserializeState(tempBlock, state)); - } catch (BadStateValueException e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%u)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - return this; - } - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public IBlockBuilder setFromName(final ResourceLocation blockResourceLocation, - final int metadata) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - final Block tempBlock = ForgeRegistries.BLOCKS.getValue(blockResourceLocation); - return this.setFromBlockState(tempBlock.getStateFromMeta(metadata)); - } - - @Override - public IBlockBuilder setFromBlockStateWithChance(final IBlockState blockState, - final int chance) { - final ResourceLocation key = blockState.getBlock().getRegistryName(); - if (!ForgeRegistries.BLOCKS.containsKey(key)) { - this.isValid = false; - } - this.blockState = blockState; - this.chance = chance; - return this; - } - - @Override - public IBlockBuilder setFromBlockWithChance(final Block block, final int chance) { - final ResourceLocation key = block.getRegistryName(); - if (!ForgeRegistries.BLOCKS.containsKey(key)) { - this.isValid = false; - } - return this.setFromBlockStateWithChance(block.getDefaultState(), chance); - } - - @Override - public IBlockBuilder setFromNameWithChance(final String blockName, final int chance) { - return this.setFromNameWithChance(new ResourceLocation(blockName), chance); - } - - @Override - public IBlockBuilder setFromNameWithChance(final String blockName, final String state, - final int chance) { - return this.setFromNameWithChance(new ResourceLocation(blockName), state, chance); - } - - @Override - public IBlockBuilder setFromNameWithChance(final String blockName, final int metadata, - final int chance) { - return this.setFromNameWithChance(new ResourceLocation(blockName), metadata, chance); - } - - @Override - public IBlockBuilder setFromNameWithChance(final ResourceLocation blockResourceLocation, - final int chance) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - return this.setFromBlockWithChance(ForgeRegistries.BLOCKS.getValue(blockResourceLocation), - chance); - } - - @Override - public IBlockBuilder setFromNameWithChance(final ResourceLocation blockResourceLocation, - final String state, final int chance) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - final Block tempBlock = ForgeRegistries.BLOCKS.getValue(blockResourceLocation); - try { - return this.setFromBlockStateWithChance(StateUtil.deserializeState(tempBlock, state), - chance); - } catch (BadStateValueException e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%d)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - return this; - } - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public IBlockBuilder setFromNameWithChance(final ResourceLocation blockResourceLocation, - final int metadata, final int chance) { - if (!ForgeRegistries.BLOCKS.containsKey(blockResourceLocation)) { - this.isValid = false; - } - final Block tempBlock = ForgeRegistries.BLOCKS.getValue(blockResourceLocation); - return this.setFromBlockStateWithChance(tempBlock.getStateFromMeta(metadata), chance); - } - - @Override - public IBlockBuilder setChance(final int chance) { - this.chance = chance; - return this; - } - - @Override - public IBlockDefinition create() { - return new BlockDefinition(this.blockState, this.chance, this.isValid); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockDefinition.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockDefinition.java deleted file mode 100644 index f30af42..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockDefinition.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.mcmoddev.orespawn.api.os3.IBlockDefinition; - -import net.minecraft.block.state.IBlockState; - -public class BlockDefinition implements IBlockDefinition { - - private final IBlockState blockState; - private final int blockChance; - private final boolean isValid; - - public BlockDefinition(final IBlockState blockState, final int chance, final boolean isValid) { - this.blockState = blockState; - this.blockChance = chance; - this.isValid = isValid; - } - - @Override - public IBlockState getBlock() { - return this.blockState; - } - - @Override - public int getChance() { - return this.blockChance; - } - - @Override - public boolean isValid() { - return this.isValid; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockList.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockList.java deleted file mode 100644 index 326ad77..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/BlockList.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.LinkedList; -import java.util.List; -import java.util.Random; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.IBlockList; -import com.mcmoddev.orespawn.api.os3.IBlockDefinition; - -import net.minecraft.block.state.IBlockState; - -public class BlockList implements IBlockList { - - private final List myBlocks; - private final List workingList; - - public BlockList() { - this.myBlocks = new LinkedList<>(); - this.workingList = new LinkedList<>(); - } - - @Override - public void addBlock(final IBlockDefinition block) { - this.myBlocks.add(block); - } - - @Override - public IBlockState getRandomBlock(final Random rand) { - if (this.workingList.isEmpty()) { - this.startNewSpawn(); - if (this.workingList.isEmpty()) { - return net.minecraft.init.Blocks.AIR.getDefaultState(); - } - } - - final int spot = rand.nextInt(this.workingList.size()); - final IBlockState rv = this.workingList.get(spot); - this.workingList.remove(spot); - return rv; - } - - @Override - public void startNewSpawn() { - this.workingList.clear(); - - this.myBlocks.stream().filter(b -> b.isValid()).forEach(b -> { - for (int i = 0; i < b.getChance(); i++) { - this.workingList.add(b.getBlock()); - } - }); - } - - @Override - public void dump() { - this.myBlocks.stream().map(bd -> bd.getBlock()).forEach( - bs -> OreSpawn.LOGGER.debug("Block %s (with state: %s)", bs.getBlock(), bs)); - } - - @Override - public int count() { - return this.myBlocks.size(); - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionBuilder.java deleted file mode 100644 index 9467beb..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionBuilder.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.LinkedList; -import java.util.List; - -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.api.os3.IDimensionBuilder; - -public class DimensionBuilder implements IDimensionBuilder { - - private final List dimensionWhitelist = new LinkedList<>(); - private final List dimensionBlacklist = new LinkedList<>(); - private boolean acceptAll = false; - private boolean denyAll = false; - private boolean acceptAllOverworld = true; - - public DimensionBuilder() { - // - } - - @Override - public IDimensionBuilder addWhitelistEntry(final int dimensionID) { - this.acceptAllOverworld = false; - this.dimensionWhitelist.add(dimensionID); - return this; - } - - @Override - public IDimensionBuilder addBlacklistEntry(final int dimensionID) { - this.acceptAllOverworld = false; - this.dimensionBlacklist.add(dimensionID); - return this; - } - - @Override - public IDimensionBuilder setAcceptAll() { - if (this.denyAll) { - this.denyAll = false; - } - this.acceptAll = true; - return this; - } - - @Override - public IDimensionBuilder setDenyAll() { - if (this.acceptAll) { - this.acceptAll = false; - } - this.denyAll = true; - return this; - } - - @Override - public IDimensionList create() { - if (this.acceptAll - || ((this.dimensionWhitelist.isEmpty()) && (this.dimensionBlacklist.isEmpty())) - && !(this.acceptAllOverworld)) { - return new DimensionListAcceptAll(); - } else if (this.denyAll) { - return new DimensionListDenyAll(); - } else if (this.acceptAllOverworld) { - return new DimensionListAcceptAllOverworld(); - } else { - return new DimensionList(this.dimensionWhitelist, this.dimensionBlacklist); - } - } - - @Override - public IDimensionBuilder setAcceptAllOverworld() { - this.acceptAll = false; - this.denyAll = false; - this.acceptAllOverworld = true; - return this; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionList.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionList.java deleted file mode 100644 index 61d9ebc..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionList.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.data.Constants; - -public class DimensionList implements com.mcmoddev.orespawn.api.IDimensionList { - - private final List whitelist = new ArrayList<>(); - private final List blacklist = new ArrayList<>(); - - public DimensionList(final List whitelist, final List blacklist) { - this.whitelist.addAll(whitelist); - this.blacklist.addAll(blacklist); - } - - @Override - public boolean matches(final int dimensionID) { - if (this.whitelist.contains(Integer.valueOf(dimensionID))) { - return true; - } - if (this.blacklist.contains(Integer.valueOf(dimensionID))) { - return false; - } - if (!this.whitelist.isEmpty()) { - return false; - } - if (!this.blacklist.isEmpty()) { - return true; - } - - // if it gets here, the whitelist and blacklist are empty... - // ***THAT*** should have resulted in a DimensionListAcceptAll being created, but... - return true; - } - - @Override - public JsonObject serialize() { - final JsonObject rv = new JsonObject(); - if (!this.whitelist.isEmpty()) { - final JsonArray wl = new JsonArray(); - whitelist.stream().forEach(wl::add); - rv.add(Constants.ConfigNames.WHITELIST, wl); - } - - if (!this.blacklist.isEmpty()) { - final JsonArray bl = new JsonArray(); - blacklist.stream().forEach(bl::add); - rv.add(Constants.ConfigNames.WHITELIST, bl); - } else if (this.whitelist.isEmpty()) { - return new DimensionListAcceptAllOverworld().serialize(); - } - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAll.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAll.java deleted file mode 100644 index 8f2b1ce..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAll.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.data.Constants.ConfigNames; - -public class DimensionListAcceptAll implements IDimensionList { - - @Override - public boolean matches(final int dimensionID) { - return true; - } - - @Override - public JsonObject serialize() { - final JsonObject rv = new JsonObject(); - rv.add(ConfigNames.BLACKLIST, new JsonArray()); - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAllOverworld.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAllOverworld.java deleted file mode 100644 index 263272c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListAcceptAllOverworld.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.data.Constants; - -public class DimensionListAcceptAllOverworld implements IDimensionList { - - @Override - public boolean matches(final int dimensionID) { - return dimensionID != -1 && dimensionID != 1; - } - - @Override - public JsonObject serialize() { - final JsonObject rv = new JsonObject(); - final JsonArray bl = new JsonArray(); - bl.add(-1); - bl.add(1); - rv.add(Constants.ConfigNames.BLACKLIST, bl); - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListDenyAll.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListDenyAll.java deleted file mode 100644 index 49eef5c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/DimensionListDenyAll.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.data.Constants; - -public class DimensionListDenyAll implements IDimensionList { - - @Override - public boolean matches(final int dimensionID) { - return false; - } - - @Override - public JsonObject serialize() { - final JsonObject rv = new JsonObject(); - rv.add(Constants.ConfigNames.WHITELIST, new JsonArray()); - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureBuilder.java deleted file mode 100644 index 7bbaa8b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureBuilder.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.Locale; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.IFeatureBuilder; -import com.mcmoddev.orespawn.api.os3.IFeatureEntry; - -import net.minecraft.util.ResourceLocation; - -public class FeatureBuilder implements IFeatureBuilder { - - private IFeature feature; - private JsonObject parameters; - private boolean useDefaults; - - public FeatureBuilder() { - this.useDefaults = false; - this.parameters = new JsonObject(); - } - - @Override - public IFeatureBuilder setFeature(final String featureName) { - String actName = featureName; - if (!actName.contains(":")) { - actName = String.format(Locale.ENGLISH, "orespawn:%s", featureName); - } - return this.setFeature(new ResourceLocation(actName)); - } - - @Override - public IFeatureBuilder setFeature(final ResourceLocation featureResourceLocation) { - if (!OreSpawn.API.featureExists(featureResourceLocation)) { - OreSpawn.LOGGER.warn( - "Feature %s is not known, feature for this will be set to the default feature", - featureResourceLocation.getPath()); - } - return this.setFeature(OreSpawn.API.getFeature(featureResourceLocation)); - } - - @Override - public IFeatureBuilder setFeature(final IFeature feature) { - this.feature = feature; - return this; - } - - @Override - public IFeatureBuilder setParameter(final String parameterName, final String parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - return this; - } - - @Override - public IFeatureBuilder setParameter(final String parameterName, final int parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - return this; - } - - @Override - public IFeatureBuilder setParameter(final String parameterName, final float parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - return this; - } - - @Override - public IFeatureBuilder setParameter(final String parameterName, final boolean parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - return this; - } - - @Override - public IFeatureBuilder setParameter(final String parameterName, - final JsonElement parameterValue) { - this.parameters.add(parameterName, parameterValue); - return this; - } - - private void setFeatureParameter(final String parameterName, final JsonElement parameterValue, - final FeatureEntry feat) { - if (parameterValue.getAsJsonPrimitive().isBoolean()) { - feat.setParameter(parameterName, parameterValue.getAsBoolean()); - } else if (parameterValue.getAsJsonPrimitive().isString()) { - feat.setParameter(parameterName, parameterValue.getAsString()); - } else { - float paramAsFloat = parameterValue.getAsFloat(); - if ((paramAsFloat - Math.floor(paramAsFloat)) > 0) { - feat.setParameter(parameterName, parameterValue.getAsFloat()); - } else { - feat.setParameter(parameterName, parameterValue.getAsInt()); - } - } - } - - @Override - public IFeatureBuilder setUseFeatureDefaults() { - this.useDefaults = true; - return this; - } - - @Override - public IFeatureEntry create() { - final FeatureEntry res = new FeatureEntry(this.feature); - if (!this.useDefaults) { - // only copy in the parameters we need - this.feature.getDefaultParameters().entrySet().stream() - .filter(ent -> !this.parameters.has(ent.getKey())) - .forEach(ent -> this.parameters.add(ent.getKey(), ent.getValue())); - } else { - // overwrite - they've said to just use the defaults - this.feature.getDefaultParameters().entrySet().stream() - .forEach(ent -> this.parameters.add(ent.getKey(), ent.getValue())); - } - - this.parameters.entrySet().stream() - .forEach(ent -> this.setFeatureParameter(ent.getKey(), ent.getValue(), res)); - - return res; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureEntry.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureEntry.java deleted file mode 100644 index c5d7167..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/FeatureEntry.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.os3.IFeatureEntry; - -public class FeatureEntry implements IFeatureEntry { - - private final IFeature feature; - private final JsonObject parameters; - - public FeatureEntry(final IFeature feature) { - this.feature = feature; - this.parameters = new JsonObject(); - } - - @Override - public IFeature getFeature() { - return this.feature; - } - - @Override - public String getFeatureName() { - return this.feature.getRegistryName().getPath(); - } - - @Override - public JsonObject getFeatureParameters() { - final JsonObject defs = feature.getDefaultParameters(); - this.parameters.entrySet().stream().forEach(ent -> defs.add(ent.getKey(), ent.getValue())); - return defs; - } - - @Override - public void setParameter(final String parameterName, final String parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - } - - @Override - public void setParameter(final String parameterName, final int parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - } - - @Override - public void setParameter(final String parameterName, final boolean parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - } - - @Override - public void setParameter(final String parameterName, final float parameterValue) { - this.parameters.addProperty(parameterName, parameterValue); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/OS3APIImpl.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/OS3APIImpl.java deleted file mode 100644 index 8366487..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/OS3APIImpl.java +++ /dev/null @@ -1,282 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.IFeature; -import com.mcmoddev.orespawn.api.exceptions.MissingVersionException; -import com.mcmoddev.orespawn.api.exceptions.NotAProperConfigException; -import com.mcmoddev.orespawn.api.exceptions.OldVersionException; -import com.mcmoddev.orespawn.api.exceptions.UnknownVersionException; -import com.mcmoddev.orespawn.api.os3.IBiomeBuilder; -import com.mcmoddev.orespawn.api.os3.IBlockBuilder; -import com.mcmoddev.orespawn.api.os3.IDimensionBuilder; -import com.mcmoddev.orespawn.api.os3.IFeatureBuilder; -import com.mcmoddev.orespawn.api.os3.IReplacementBuilder; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.api.os3.ISpawnBuilder; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.api.os3.OS3API; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.FeatureRegistry; -import com.mcmoddev.orespawn.data.PresetsStorage; -import com.mcmoddev.orespawn.data.ReplacementsRegistry; -import com.mcmoddev.orespawn.json.OreSpawnReader; -import com.mcmoddev.orespawn.worldgen.OreSpawnFeatureGenerator; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.crash.CrashReport; -import net.minecraft.util.ResourceLocation; - -public class OS3APIImpl implements OS3API { - - private static final Map spawns; - private static final List generators; - private static final FeatureRegistry features; - private static final ReplacementsRegistry replacements; - private static final PresetsStorage presets; - private static final String ORE_SPAWN_VERSION = "OreSpawn Version"; - private static final Map spawnsToSourceFiles = new TreeMap<>(); - - static { - spawns = new ConcurrentHashMap<>(); - features = new FeatureRegistry(); - replacements = new ReplacementsRegistry(); - presets = new PresetsStorage(); - generators = new LinkedList<>(); - } - - public OS3APIImpl() { - // - } - - public void loadConfigFiles() { - final String failedReadingConfigsFrom = "Failed reading configs from "; - PathMatcher featuresFiles = FileSystems.getDefault() - .getPathMatcher("glob:**/features-*.json"); - PathMatcher replacementsFiles = FileSystems.getDefault() - .getPathMatcher("glob:**/replacements-*.json"); - PathMatcher jsonMatcher = FileSystems.getDefault().getPathMatcher("glob:**/*.json"); - - try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { - stream.filter(featuresFiles::matches).map(Path::toFile) - .forEach(features::loadFeaturesFile); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - failedReadingConfigsFrom + Constants.SYSCONF.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - - // have to do this twice or we have issues - try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { - stream.filter(replacementsFiles::matches).forEach(replacements::loadFile); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - failedReadingConfigsFrom + Constants.SYSCONF.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - - if (Constants.SYSCONF.resolve("presets-default.json").toFile().exists()) { - presets.load(Constants.SYSCONF.resolve("presets-default.json")); - } - - try (final Stream stream = Files.walk(Constants.CONFDIR, 1)) { - stream.filter(jsonMatcher::matches).forEach(conf -> { - try { - OreSpawnReader.tryReadFile(conf); - } catch (final MissingVersionException | NotAProperConfigException - | OldVersionException | UnknownVersionException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed reading config " + conf.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - }); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - failedReadingConfigsFrom + Constants.CONFDIR.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } - - @Override - public void addGenerator(final OreSpawnFeatureGenerator generator) { - generators.add(generator); - } - - @Override - public List getGenerators() { - return new LinkedList(generators); - } - - @Override - public void addSpawn(final ISpawnEntry spawnEntry) { - if (spawnEntry != null) { - spawns.put(new ResourceLocation(spawnEntry.getSpawnName()), spawnEntry); - } - } - - @Override - public void addFeature(final String featureName, final IFeature feature) { - features.addFeature(featureName, feature); - } - - @Override - public void addReplacement(final IReplacementEntry replacementEntry) { - replacements.addReplacement(replacementEntry); - } - - @Override - public ISpawnBuilder getSpawnBuilder() { - return new SpawnBuilder(); - } - - @Override - public IDimensionBuilder getDimensionBuilder() { - return new DimensionBuilder(); - } - - @Override - public IFeatureBuilder getFeatureBuilder() { - return new FeatureBuilder(); - } - - @Override - public IBlockBuilder getBlockBuilder() { - return new BlockBuilder(); - } - - @Override - public IBiomeBuilder getBiomeBuilder() { - return new BiomeBuilder(); - } - - @Override - public IReplacementBuilder getReplacementBuilder() { - return new ReplacementBuilder(); - } - - @Override - public Map getReplacements() { - final Map temp = new HashMap<>(); - replacements.getReplacements().entrySet().stream() - .forEach(e -> temp.put(e.getKey().getPath(), e.getValue())); - return ImmutableMap.copyOf(temp); - } - - @Override - public IReplacementEntry getReplacement(final String replacementName) { - return replacements.getReplacement(replacementName); - } - - @Override - public List getSpawns(final int dimensionID) { - return ImmutableList.copyOf( - spawns.entrySet().stream().filter(e -> e.getValue().dimensionAllowed(dimensionID)) - .map(Map.Entry::getValue).collect(Collectors.toList())); - } - - @Override - public ISpawnEntry getSpawn(final String spawnName) { - return spawns.get(new ResourceLocation(spawnName)); - } - - @Override - public Map getAllSpawns() { - final Map sp = new HashMap<>(); - spawns.entrySet().forEach(ent -> sp.put(ent.getKey().getPath(), ent.getValue())); - return ImmutableMap.copyOf(sp); - } - - @Override - public boolean featureExists(final String featureName) { - return this.featureExists(new ResourceLocation(featureName.contains(":") ? featureName - : String.format(Locale.ENGLISH, "orespawn:%s", featureName))); - } - - @Override - public boolean featureExists(final ResourceLocation featureName) { - return features.hasFeature(featureName); - } - - @Override - public IFeature getFeature(final String featureName) { - return this.getFeature(new ResourceLocation(featureName)); - } - - @Override - public IFeature getFeature(final ResourceLocation featureName) { - return features.getFeature(featureName); - } - - @Override - public PresetsStorage copyPresets() { - final PresetsStorage copy = new PresetsStorage(); - presets.copy(copy); - return copy; - } - - @Override - public List getDimensionDefaultReplacements(final int dimensionID) { - return replacements.getDimensionDefault(dimensionID); - } - - @Override - public boolean hasReplacement(final ResourceLocation resourceLocation) { - return replacements.has(resourceLocation); - } - - @Override - public boolean hasReplacement(final String name) { - return this.hasReplacement(new ResourceLocation( - name.contains(":") ? name : String.format(Locale.ENGLISH, "orespawn:%s", name))); - } - - @Override - public void mapEntryToFile(final Path p, final String entryName) { - spawnsToSourceFiles.put(entryName, p); - } - - @Override - public List getSpawnsForFile(final String fileName) { - final Path p = Constants.CONFDIR.resolve(fileName); - final List values = spawnsToSourceFiles.entrySet().stream() - .filter(ent -> ent.getValue().equals(p)).map(Map.Entry::getKey) - .collect(Collectors.toList()); - return ImmutableList.copyOf(values); - } - - @Override - public Map> getSpawnsByFile() { - final Map> temp = new HashMap<>(); - spawnsToSourceFiles.entrySet().stream().forEach(ent -> { - if (temp.containsKey(ent.getValue())) { - temp.get(ent.getValue()).add(ent.getKey()); - } else { - temp.put(ent.getValue(), Lists.newLinkedList(Arrays.asList(ent.getKey()))); - } - }); - - return ImmutableMap.copyOf(temp); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementBuilder.java deleted file mode 100644 index 8071d4b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementBuilder.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -import org.apache.commons.lang3.RandomStringUtils; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.exceptions.BadStateValueException; -import com.mcmoddev.orespawn.api.os3.IReplacementBuilder; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class ReplacementBuilder implements IReplacementBuilder { - - private String replacementName = null; - private List entries; - - public ReplacementBuilder() { - this.entries = new LinkedList<>(); - } - - @Override - public IReplacementBuilder setFromName(final String entryName) { - this.replacementName = entryName; - this.entries.addAll(OreSpawn.API.getReplacement(entryName).getEntries()); - return this; - } - - @Override - public IReplacementBuilder setName(final String name) { - this.replacementName = name; - return this; - } - - @Override - public IReplacementBuilder addEntry(final IBlockState blockState) { - this.entries.add(blockState); - return this; - } - - @Override - public IReplacementBuilder addEntry(final String blockName) { - return this.addEntry(new ResourceLocation(blockName)); - } - - @Override - public IReplacementBuilder addEntry(final String blockName, final String state) { - return this.addEntry(new ResourceLocation(blockName), state); - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public IReplacementBuilder addEntry(final String blockName, final int metadata) { - return this.addEntry(new ResourceLocation(blockName), metadata); - } - - @Override - public IReplacementBuilder addEntry(final ResourceLocation blockResourceLocation) { - return this - .addEntry(ForgeRegistries.BLOCKS.getValue(blockResourceLocation).getDefaultState()); - } - - @Override - public IReplacementBuilder addEntry(final ResourceLocation blockResourceLocation, - final String state) { - try { - return this.addEntry(StateUtil - .deserializeState(ForgeRegistries.BLOCKS.getValue(blockResourceLocation), state)); - } catch (BadStateValueException e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%u)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - return this.addEntry(ForgeRegistries.BLOCKS.getValue(blockResourceLocation).getDefaultState()); - } - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public IReplacementBuilder addEntry(final ResourceLocation blockResourceLocation, - final int metadata) { - return this.addEntry( - ForgeRegistries.BLOCKS.getValue(blockResourceLocation).getStateFromMeta(metadata)); - } - - @Override - public boolean hasEntries() { - return !this.entries.isEmpty(); - } - - @Override - public IReplacementEntry create() { - if (this.replacementName == null) { - this.replacementName = String.format(Locale.ENGLISH, "replacement_%s", - RandomStringUtils.randomAlphanumeric(8, 16)); - } - - return new ReplacementEntry(this.replacementName, this.entries); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementEntry.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementEntry.java deleted file mode 100644 index e2e21fc..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/ReplacementEntry.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.api.os3.OreSpawnBlockMatcher; - -import net.minecraft.block.state.IBlockState; -import net.minecraftforge.registries.IForgeRegistryEntry; - -public class ReplacementEntry extends IForgeRegistryEntry.Impl - implements IReplacementEntry { - - private final List matchVal; - private OreSpawnBlockMatcher matcher = null; - - public ReplacementEntry(final String name, final IBlockState... toMatch) { - super.setRegistryName(name); - this.matchVal = Arrays.asList(toMatch); - } - - public ReplacementEntry(final String name, final List toMatch) { - super.setRegistryName(name); - this.matchVal = new ArrayList<>(); - this.matchVal.addAll(toMatch); - } - - @Override - public OreSpawnBlockMatcher getMatcher() { - if (this.matcher == null) { - this.matcher = new OreSpawnBlockMatcher(this.matchVal); - } - return this.matcher; - } - - public List getEntries() { - return ImmutableList.copyOf(this.matchVal); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnBuilder.java deleted file mode 100644 index 2d9dac8..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnBuilder.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.api.IBlockList; -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.api.exceptions.BadStateValueException; -import com.mcmoddev.orespawn.api.os3.IBlockDefinition; -import com.mcmoddev.orespawn.api.os3.IFeatureEntry; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.api.os3.ISpawnBuilder; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class SpawnBuilder implements ISpawnBuilder { - - private String spawnName; - private boolean enabled; - private boolean retrogen; - private IBlockList blocks; - private IFeatureEntry feature; - private BiomeLocation biomes; - private IDimensionList dimensions; - private IReplacementEntry replacements; - - public SpawnBuilder() { - this.enabled = false; - this.retrogen = false; - this.blocks = new BlockList(); - } - - public SpawnBuilder(final String spawnName) { - this(); - this.spawnName = spawnName; - } - - @Override - public ISpawnBuilder setName(final String name) { - this.spawnName = name; - return this; - } - - @Override - public ISpawnBuilder setDimensions(final IDimensionList dimensions) { - this.dimensions = dimensions; - return this; - } - - @Override - public ISpawnBuilder setBiomes(final BiomeLocation biomes) { - this.biomes = biomes; - return this; - } - - @Override - public ISpawnBuilder setEnabled(final boolean enabled) { - this.enabled = enabled; - return this; - } - - @Override - public ISpawnBuilder setRetrogen(final boolean retrogen) { - this.retrogen = retrogen; - return this; - } - - @Override - public ISpawnBuilder setReplacement(final IReplacementEntry replacements) { - this.replacements = replacements; - return this; - } - - @Override - public ISpawnBuilder setFeature(final IFeatureEntry feature) { - this.feature = feature; - return this; - } - - @Override - public ISpawnBuilder addBlock(final String blockName) { - return this.addBlock(new ResourceLocation(blockName)); - } - - @Override - public ISpawnBuilder addBlock(final String blockName, final String blockState) { - return this.addBlock(new ResourceLocation(blockName), blockState); - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public ISpawnBuilder addBlock(final String blockName, final int blockMetadata) { - return this.addBlock(new ResourceLocation(blockName), blockMetadata); - } - - @Override - public ISpawnBuilder addBlock(final ResourceLocation blockResourceLocation) { - return this.addBlockWithChance(blockResourceLocation, 100); - } - - @Override - public ISpawnBuilder addBlock(final ResourceLocation blockResourceLocation, - final String blockState) { - return this.addBlockWithChance(blockResourceLocation, blockState, 100); - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public ISpawnBuilder addBlock(final ResourceLocation blockResourceLocation, - final int blockMetadata) { - return this.addBlockWithChance(blockResourceLocation, 100); - } - - @Override - public ISpawnBuilder addBlock(final Block block) { - return this.addBlockWithChance(block, 100); - } - - @Override - public ISpawnBuilder addBlock(final IBlockState block) { - return this.addBlockWithChance(block, 100); - } - - @Override - public ISpawnBuilder addBlockWithChance(final String blockName, final int chance) { - return this.addBlockWithChance(new ResourceLocation(blockName), chance); - } - - @Override - public ISpawnBuilder addBlockWithChance(final String blockName, final String blockState, - final int chance) { - return this.addBlockWithChance(new ResourceLocation(blockName), blockState, chance); - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public ISpawnBuilder addBlockWithChance(final String blockName, final int blockMetadata, - final int chance) { - return this.addBlockWithChance(blockName, blockMetadata, chance); - } - - @Override - public ISpawnBuilder addBlockWithChance(final ResourceLocation blockResourceLocation, - final int chance) { - final IBlockState tempVar = ForgeRegistries.BLOCKS.getValue(blockResourceLocation) - .getDefaultState(); - return this.addBlockWithChance(tempVar, chance); - } - - @Override - public ISpawnBuilder addBlockWithChance(final ResourceLocation blockResourceLocation, - final String blockState, final int chance) { - final Block tempBlock = ForgeRegistries.BLOCKS.getValue(blockResourceLocation); - IBlockState tempVar; - try { - tempVar = StateUtil.deserializeState(tempBlock, blockState); - } catch (BadStateValueException e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%u)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - tempVar = tempBlock.getDefaultState(); - } - return this.addBlockWithChance(tempVar, chance); - } - - /** - * - * @deprecated - */ - @Override - @Deprecated - public ISpawnBuilder addBlockWithChance(final ResourceLocation blockResourceLocation, - final int blockMetadata, final int chance) { - final IBlockState tempVar = ForgeRegistries.BLOCKS.getValue(blockResourceLocation) - .getStateFromMeta(blockMetadata); - return this.addBlockWithChance(tempVar, chance); - } - - @Override - public ISpawnBuilder addBlockWithChance(final Block block, final int chance) { - final IBlockState tempVar = block.getDefaultState(); - return this.addBlockWithChance(tempVar, chance); - } - - @Override - public ISpawnBuilder addBlockWithChance(final IBlockState block, final int chance) { - final BlockBuilder bb = new BlockBuilder(); - bb.setFromBlockStateWithChance(block, chance); - return this.addBlock(bb.create()); - } - - @Override - public ISpawnBuilder addBlock(final IBlockDefinition block) { - if (block.isValid()) { - this.blocks.addBlock(block); - } - return this; - } - - @Override - public SpawnEntry create() { - if (this.blocks.count() > 0) { - if (this.dimensions == null) this.dimensions = new DimensionBuilder().setAcceptAll().create(); - if (this.biomes == null) this.biomes = new BiomeBuilder().setAcceptAll().create(); - if (this.replacements == null) this.replacements = com.mcmoddev.orespawn.OreSpawn.API.getReplacement("default"); - if (this.feature == null) { - com.mcmoddev.orespawn.OreSpawn.LOGGER.fatal("Spawn entry {} does not have a stated feature, ignoring.", this.spawnName); - return null; - } - return new SpawnEntry(this.spawnName, this.enabled, this.retrogen, this.dimensions, - this.biomes, this.replacements, this.blocks, this.feature); - } else { - return null; - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnEntry.java b/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnEntry.java deleted file mode 100644 index 8aa7258..0000000 --- a/src/main/java/com/mcmoddev/orespawn/impl/os3/SpawnEntry.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.mcmoddev.orespawn.impl.os3; - -import java.util.Random; - -import com.mcmoddev.orespawn.api.BiomeLocation; -import com.mcmoddev.orespawn.api.IBlockList; -import com.mcmoddev.orespawn.api.IDimensionList; -import com.mcmoddev.orespawn.api.os3.IFeatureEntry; -import com.mcmoddev.orespawn.api.os3.IReplacementEntry; -import com.mcmoddev.orespawn.api.os3.OreSpawnBlockMatcher; - -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class SpawnEntry implements com.mcmoddev.orespawn.api.os3.ISpawnEntry { - - private final String spawnName; - private final IDimensionList dimensions; - private final IReplacementEntry replacements; - private final IBlockList blocks; - private final BiomeLocation biomes; - private final IFeatureEntry feature; - private final boolean enabled; - private final boolean retrogen; - - public SpawnEntry(final String spawnName, final boolean enabled, final boolean retrogen, - final IDimensionList dimensions, final BiomeLocation biomes, - final IReplacementEntry replacements, final IBlockList blocks, - final IFeatureEntry feature) { - this.spawnName = spawnName; - this.enabled = enabled; - this.retrogen = retrogen; - this.dimensions = dimensions; - this.biomes = biomes; - this.replacements = replacements; - this.blocks = blocks; - this.feature = feature; - } - - @Override - public boolean isRetrogen() { - return this.retrogen; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - @Override - public String getSpawnName() { - return this.spawnName; - } - - @Override - public boolean dimensionAllowed(final int dimension) { - return this.dimensions.matches(dimension); - } - - @Override - public boolean biomeAllowed(final ResourceLocation biomeName) { - return this.biomeAllowed(ForgeRegistries.BIOMES.getValue(biomeName)); - } - - @Override - public boolean biomeAllowed(final Biome biome) { - return this.biomes.matches(biome); - } - - @Override - public IFeatureEntry getFeature() { - return this.feature; - } - - @Override - public OreSpawnBlockMatcher getMatcher() { - return this.replacements.getMatcher(); - } - - @Override - public IBlockList getBlocks() { - return this.blocks; - } - - @Override - public void generate(final Random random, final World world, - final IChunkGenerator chunkGenerator, final IChunkProvider chunkProvider, - final ChunkPos pos) { - this.feature.getFeature().setRandom(random); - this.feature.getFeature().generate(world, chunkGenerator, chunkProvider, this, pos); - } - - @Override - public IDimensionList getDimensions() { - return this.dimensions; - } - - @Override - public BiomeLocation getBiomes() { - return this.biomes; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/json/OreSpawnReader.java b/src/main/java/com/mcmoddev/orespawn/json/OreSpawnReader.java deleted file mode 100644 index 8d30a8b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/json/OreSpawnReader.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.mcmoddev.orespawn.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.AbstractMap; -import java.util.Arrays; -import java.util.List; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.exceptions.BadStateValueException; -import com.mcmoddev.orespawn.api.exceptions.BadValueException; -import com.mcmoddev.orespawn.api.exceptions.MissingVersionException; -import com.mcmoddev.orespawn.api.exceptions.NotAProperConfigException; -import com.mcmoddev.orespawn.api.exceptions.OldVersionException; -import com.mcmoddev.orespawn.api.exceptions.UnknownFieldException; -import com.mcmoddev.orespawn.api.exceptions.UnknownNameException; -import com.mcmoddev.orespawn.api.exceptions.UnknownVersionException; -import com.mcmoddev.orespawn.api.os3.IBiomeBuilder; -import com.mcmoddev.orespawn.api.os3.IBlockBuilder; -import com.mcmoddev.orespawn.api.os3.IDimensionBuilder; -import com.mcmoddev.orespawn.api.os3.IFeatureBuilder; -import com.mcmoddev.orespawn.api.os3.IReplacementBuilder; -import com.mcmoddev.orespawn.api.os3.ISpawnBuilder; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.PresetsStorage; -import com.mcmoddev.orespawn.util.StateUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.crash.CrashReport; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.BiomeDictionary; -import net.minecraftforge.fml.common.registry.ForgeRegistries; -import net.minecraftforge.oredict.OreDictionary; - -public class OreSpawnReader { - - private static final String ORE_SPAWN_VERSION = "OreSpawn Version"; - - private OreSpawnReader() { - // hiding the default one - } - - private static JsonElement doPresetFix(final JsonElement value, - final PresetsStorage configPresets) { - if (value.isJsonObject()) { - return doPresetForObject(value.getAsJsonObject(), configPresets); - } else if (value.isJsonArray()) { - return doPresetForArray(value.getAsJsonArray(), configPresets); - } else if (value.isJsonPrimitive() && !value.isJsonNull()) { - if (value.getAsJsonPrimitive().isString() && value.getAsString().matches("^\\$.*")) { - return configPresets.get(value.getAsString()); - } else { - return value; - } - } else { - OreSpawn.LOGGER.error("Error handling presets for config, unknown value type for item " - + value.toString()); - return value; - } - } - - public static void tryReadFile(final Path conf) - throws MissingVersionException, NotAProperConfigException, OldVersionException, - UnknownVersionException { - final JsonParser parser = new JsonParser(); - - try (BufferedReader data = Files.newBufferedReader(conf)) { - final JsonElement json = parser.parse(data); - - if (!json.isJsonObject()) { - throw new NotAProperConfigException(); - } - - final JsonObject root = json.getAsJsonObject(); - if (!root.has(Constants.ConfigNames.FILE_VERSION)) { - throw new MissingVersionException(); - } - - final float version = root.get(Constants.ConfigNames.FILE_VERSION).getAsFloat(); - if (version < 2f) { - throw new OldVersionException(); - } else if (version != 2f) { - throw new UnknownVersionException(); - } - - if (!root.has(Constants.ConfigNames.SPAWNS)) { - throw new NotAProperConfigException(); - } - - final JsonObject spawnData = doHandlePresets(root).get(Constants.ConfigNames.SPAWNS) - .getAsJsonObject(); - spawnData.entrySet().stream().forEach(e -> { - try { - OreSpawn.API.mapEntryToFile(conf, e.getKey()); - loadSingleEntry(e); - } catch (UnknownFieldException | BadValueException | UnknownNameException e1) { - CrashReport report = CrashReport.makeCrashReport(e1, - "Error parsing an entry " + e.getKey() + " in " + conf.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - }); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed reading config data " + conf.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } catch (final JsonIOException | JsonSyntaxException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "JSON Parsing Error in " + conf.toString()); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } - - private static JsonObject doHandlePresets(final JsonObject spawnData) { - final PresetsStorage configPresets = OreSpawn.API.copyPresets(); - if (spawnData.has(Constants.ConfigNames.PRESETS)) { - spawnData.get(Constants.ConfigNames.PRESETS).getAsJsonObject().entrySet().stream() - .forEach(entry -> { - String section = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet().stream() - .forEach(sect -> configPresets.setSymbolSection(section, - sect.getKey(), sect.getValue())); - }); - } - - final JsonObject spawnDataFixed = new JsonObject(); - for (final Entry elem : spawnData.get(Constants.ConfigNames.SPAWNS) - .getAsJsonObject().entrySet()) { - spawnDataFixed.add(elem.getKey(), doPresetFix(elem.getValue(), configPresets)); - } - return spawnData; - } - - private static JsonElement doPresetForArray(final JsonArray value, - final PresetsStorage configPresets) { - final JsonArray rv = new JsonArray(); - value.forEach(it -> rv.add(doPresetFix(it, configPresets))); - return rv; - } - - private static JsonElement doPresetForObject(final JsonObject value, - final PresetsStorage configPresets) { - final JsonObject rv = new JsonObject(); - - value.entrySet().stream().forEach( - entry -> rv.add(entry.getKey(), doPresetFix(entry.getValue(), configPresets))); - return rv; - } - - public static void loadFromJson(final String name, final JsonElement json) { - final Entry t = new AbstractMap.SimpleEntry<>(name, json); - try { - loadSingleEntry(t); - } catch (UnknownFieldException | BadValueException | UnknownNameException e) { - final CrashReport report = CrashReport.makeCrashReport(e, - "Error parsing an manual JSON read for " + name); - report.getCategory().addCrashSection(ORE_SPAWN_VERSION, Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - } - - private static void loadSingleEntry(final Entry entry) - throws UnknownFieldException, BadValueException, UnknownNameException { - final ISpawnBuilder sb = OreSpawn.API.getSpawnBuilder(); - final IFeatureBuilder fb = OreSpawn.API.getFeatureBuilder(); - sb.setName(entry.getKey()); - for (final Entry ent : entry.getValue().getAsJsonObject().entrySet()) { - switch (ent.getKey()) { - case Constants.ConfigNames.RETROGEN: - sb.setRetrogen(ent.getValue().getAsBoolean()); - break; - case Constants.ConfigNames.ENABLED: - sb.setEnabled(ent.getValue().getAsBoolean()); - break; - case Constants.ConfigNames.DIMENSIONS: - final IDimensionBuilder db = OreSpawn.API.getDimensionBuilder(); - loadDimensionEntry(db, ent); - sb.setDimensions(db.create()); - break; - case Constants.ConfigNames.BIOMES: - if (!ent.getValue().isJsonObject()) { - throw new BadValueException(Constants.ConfigNames.BIOMES, - ent.getValue().toString()); - } - final IBiomeBuilder bb = OreSpawn.API.getBiomeBuilder(); - loadBiomes(bb, ent.getValue().getAsJsonObject()); - sb.setBiomes(bb.create()); - break; - case Constants.ConfigNames.FEATURE: - fb.setFeature(loadFeatureEntry(ent)); - break; - case Constants.ConfigNames.REPLACEMENT: - final IReplacementBuilder rb = OreSpawn.API.getReplacementBuilder(); - loadReplacements(rb, ent); - - if (rb.hasEntries()) { - sb.setReplacement(rb.create()); - } - break; - case Constants.ConfigNames.BLOCK: - loadBlocks(sb, ent); - break; - case Constants.ConfigNames.PARAMETERS: - if (ent.getValue().isJsonObject()) { - ent.getValue().getAsJsonObject().entrySet().stream() - .forEach(e -> fb.setParameter(e.getKey(), e.getValue())); - } - break; - default: - throw new UnknownFieldException(ent.getKey()); - } - } - sb.setFeature(fb.create()); - OreSpawn.API.addSpawn(sb.create()); - } - - private static String loadFeatureEntry(Entry ent) throws BadValueException, UnknownNameException { - if (ent.getValue().isJsonPrimitive() - && !ent.getValue().getAsJsonPrimitive().isString()) { - throw new BadValueException(Constants.ConfigNames.FEATURE, - ent.getValue().toString()); - } - final String featureName = ent.getValue().getAsString(); - if (!OreSpawn.API.featureExists(featureName)) { - throw new UnknownNameException(Constants.ConfigNames.FEATURE, featureName); - } - return featureName; - } - - @SuppressWarnings("deprecation") - private static void loadBlocks(ISpawnBuilder sb, Entry ent) throws BadValueException { - if (ent.getValue().isJsonArray()) { - for (final JsonElement elem : ent.getValue().getAsJsonArray()) { - final IBlockBuilder block = OreSpawn.API.getBlockBuilder(); - if (elem.isJsonObject()) { - final JsonObject bl = elem.getAsJsonObject(); - if (bl.has(Constants.ConfigNames.STATE)) { - block.setFromNameWithChance( - bl.get(Constants.ConfigNames.NAME).getAsString(), - bl.get(Constants.ConfigNames.STATE).getAsString(), - bl.get(Constants.ConfigNames.CHANCE).getAsInt()); - } else if (bl.has(Constants.ConfigNames.METADATA)) { - block.setFromNameWithChance( - bl.get(Constants.ConfigNames.NAME).getAsString(), - bl.get(Constants.ConfigNames.METADATA).getAsInt(), - bl.get(Constants.ConfigNames.CHANCE).getAsInt()); - } else { - block.setFromNameWithChance( - bl.get(Constants.ConfigNames.NAME).getAsString(), - bl.get(Constants.ConfigNames.CHANCE).getAsInt()); - } - sb.addBlock(block.create()); - } else { - OreSpawn.LOGGER.error( - "Skipping value {} in blocks list as it is not the correct format", - elem.toString()); - } - } - } else { - throw new BadValueException(Constants.ConfigNames.BLOCK, - ent.getValue().toString()); - } - } - - private static void loadReplacements(IReplacementBuilder rb, Entry ent) throws BadValueException { - if (!ent.getValue().isJsonArray() - && !ent.getValue().getAsJsonPrimitive().isString()) { - throw new BadValueException(Constants.ConfigNames.REPLACEMENT, - ent.getValue().toString()); - } else if (ent.getValue().isJsonPrimitive() - && ent.getValue().getAsJsonPrimitive().isString()) { - if (OreSpawn.API.hasReplacement(ent.getValue().getAsString())) { - rb.setFromName(ent.getValue().getAsString()); - } - } else { - for (final JsonElement e : ent.getValue().getAsJsonArray()) { - if (e.isJsonObject()) { - loadBlock(e.getAsJsonObject()).stream().forEach(rb::addEntry); - } else { - OreSpawn.LOGGER.error( - "Skipping value {} in replacements list as it is not the correct format", - e.toString()); - } - } - } - } - - private static void loadDimensionEntry(IDimensionBuilder db, Entry ent) throws BadValueException { - if (ent.getValue().isJsonArray()) { - final JsonArray dims = ent.getValue().getAsJsonArray(); - if (dims.size() == 0) { - // blank list, accept all overworld - db.setAcceptAllOverworld(); - } else { - dims.forEach(item -> { - if (item.isJsonPrimitive() - && item.getAsJsonPrimitive().isNumber()) { - db.addWhitelistEntry(item.getAsInt()); - } - }); - } - } else if (ent.getValue().isJsonObject()) { - loadDimensions(db, ent.getValue().getAsJsonObject()); - } else { - throw new BadValueException(Constants.ConfigNames.DIMENSIONS, - ent.getValue().toString()); - } - } - - @SuppressWarnings("deprecation") - private static List loadBlock(final JsonObject json) { - final String blockName = json.get(Constants.ConfigNames.NAME).getAsString(); - if (json.has(Constants.ConfigNames.STATE)) { - final Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName)); - try { - return Arrays.asList(StateUtil.deserializeState(block, - json.get(Constants.ConfigNames.STATE).getAsString())); - } catch (BadStateValueException e) { - StringBuilder p = new StringBuilder(); - for(StackTraceElement elem: e.getStackTrace()) p.append(String.format("%s.%s (%s:%u)\n", elem.getClassName(), elem.getMethodName(), elem.getFileName(), elem.getLineNumber())); - OreSpawn.LOGGER.error(String.format("Exception: %s\n%s", e.getMessage(), p.toString())); - return Arrays.asList(block.getDefaultState()); - } - } else if (json.has(Constants.ConfigNames.METADATA)) { - final Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName)); - return Arrays.asList( - block.getStateFromMeta(json.get(Constants.ConfigNames.METADATA).getAsInt())); - } - - if (blockName.startsWith("ore:")) { - final String entry = blockName.split(":")[1]; - return ImmutableList.copyOf(OreDictionary.getOres(entry, false).stream().map(is -> { - final Block b = Block.getBlockFromItem(is.getItem()); - return b.getStateFromMeta(is.getMetadata()); - }).collect(Collectors.toList())); - } - final Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(blockName)); - return Arrays.asList(block.getDefaultState()); - } - - private static final String WHITELIST_ERROR = "Skipping entry (%s) in whitelist, not a proper value"; - private static final String BLACKLIST_ERROR = "Skipping entry (%s) in blacklist, not a proper value"; - private static void loadBiomes(final IBiomeBuilder bb, final JsonObject biomeList) { - boolean emptyWhitelist = false; - - if (biomeList.has(Constants.ConfigNames.WHITELIST) - && biomeList.get(Constants.ConfigNames.WHITELIST).getAsJsonArray().size() > 0) { - loadBiomeWhitelist(bb, biomeList.get(Constants.ConfigNames.WHITELIST).getAsJsonArray()); - } else { - emptyWhitelist = true; - } - - if (biomeList.has(Constants.ConfigNames.BLACKLIST) - && biomeList.get(Constants.ConfigNames.BLACKLIST).getAsJsonArray().size() > 0) { - loadBiomeBlacklist(bb, biomeList.get(Constants.ConfigNames.BLACKLIST).getAsJsonArray()); - } else if (emptyWhitelist) { - // empty whitelist and blacklist - accept everything - bb.setAcceptAll(); - } - } - - private static void loadBiomeBlacklist(IBiomeBuilder bb, JsonArray blacklist) { - for (final JsonElement elem : blacklist) { - if (elem.isJsonPrimitive() && elem.getAsJsonPrimitive().isString()) { - final String xN = elem.getAsString(); - if (xN.contains(":")) { - // not a BiomeDictionary entry (we hope) - bb.addBlacklistEntry(xN); - } else { - BiomeDictionary.getBiomes(BiomeDictionary.Type.getType(xN)).stream() - .forEach(bb::addBlacklistEntry); - } - } else { - OreSpawn.LOGGER.error(BLACKLIST_ERROR, elem.getAsString()); - } - } - } - - private static void loadBiomeWhitelist(IBiomeBuilder bb, JsonArray whitelist) { - for (final JsonElement elem : whitelist) { - if (elem.isJsonPrimitive() && elem.getAsJsonPrimitive().isString()) { - final String xN = elem.getAsString(); - if (xN.contains(":")) { - // not a BiomeDictionary entry (we hope) - bb.addWhitelistEntry(xN); - } else { - BiomeDictionary.getBiomes(BiomeDictionary.Type.getType(xN)).stream() - .forEach(bb::addWhitelistEntry); - } - } else { - OreSpawn.LOGGER.error(WHITELIST_ERROR, elem.getAsString()); - } - } - } - - private static void loadDimensions(final IDimensionBuilder db, final JsonObject dimensionList) { - boolean emptyWhitelist = false; - - if (dimensionList.has(Constants.ConfigNames.WHITELIST) - && dimensionList.get(Constants.ConfigNames.WHITELIST).getAsJsonArray().size() > 0) { - loadDimensionWhitelist(db, dimensionList.get(Constants.ConfigNames.WHITELIST).getAsJsonArray()); - } else { - emptyWhitelist = true; - } - - if (dimensionList.has(Constants.ConfigNames.BLACKLIST) - && dimensionList.get(Constants.ConfigNames.BLACKLIST).getAsJsonArray().size() > 0) { - loadDimensionBlacklist(db, dimensionList.get(Constants.ConfigNames.BLACKLIST).getAsJsonArray()); - } else if (emptyWhitelist) { - db.setAcceptAllOverworld(); - } - } - - private static void loadDimensionBlacklist(IDimensionBuilder db, JsonArray blacklist) { - for (final JsonElement elem : blacklist) { - if (elem.isJsonPrimitive() && elem.getAsJsonPrimitive().isNumber()) { - db.addBlacklistEntry(elem.getAsInt()); - } else { - OreSpawn.LOGGER.error(WHITELIST_ERROR, elem.getAsString()); - } - } - } - - private static void loadDimensionWhitelist(IDimensionBuilder db, JsonArray whitelist) { - for (final JsonElement elem : whitelist) { - if (elem.isJsonPrimitive() && elem.getAsJsonPrimitive().isNumber()) { - db.addWhitelistEntry(elem.getAsInt()); - } else { - OreSpawn.LOGGER.error(BLACKLIST_ERROR, elem.getAsString()); - } - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/json/OreSpawnWriter.java b/src/main/java/com/mcmoddev/orespawn/json/OreSpawnWriter.java deleted file mode 100644 index f8edba4..0000000 --- a/src/main/java/com/mcmoddev/orespawn/json/OreSpawnWriter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.mcmoddev.orespawn.json; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.Constants.ConfigNames; - -import net.minecraft.crash.CrashReport; - -public class OreSpawnWriter { - - /* - * Write out the configs as the system knows them to the 'forced-saves' directory - */ - public static void saveConfigs() { - final Map> configs = OreSpawn.API.getSpawnsByFile(); - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - configs.entrySet().stream().forEach(ent -> saveSingle(ent.getKey(), gson)); - } - - private static void saveSingle(final Path filePath, final Gson gson) { - final JsonObject root = new JsonObject(); - root.addProperty(ConfigNames.FILE_VERSION, "2.0"); - final JsonObject spawns = new JsonObject(); - final String k = filePath.getFileName().toString(); - final Path saveDir = Constants.CONFDIR.resolve("forced-saves"); - final Path conf = saveDir.resolve(k); - if (!saveDir.toFile().exists()) { - saveDir.toFile().mkdirs(); - } - try (final BufferedWriter p = Files.newBufferedWriter(conf, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { - OreSpawn.API.getSpawnsForFile(k).stream().forEach(spawnName -> { - final JsonObject thisSpawn = new JsonObject(); - final ISpawnEntry spawnEntry = OreSpawn.API.getSpawn(spawnName); - thisSpawn.addProperty(ConfigNames.ENABLED, spawnEntry.isEnabled()); - thisSpawn.addProperty(ConfigNames.RETROGEN, spawnEntry.isRetrogen()); - thisSpawn.addProperty(ConfigNames.FEATURE, - spawnEntry.getFeature().getFeatureName()); - thisSpawn.add(ConfigNames.DIMENSIONS, spawnEntry.getDimensions().serialize()); - thisSpawn.add(ConfigNames.BIOMES, spawnEntry.getBiomes().serialize()); - thisSpawn.add(ConfigNames.REPLACEMENT, spawnEntry.getMatcher().serialize()); - thisSpawn.add(ConfigNames.PARAMETERS, - spawnEntry.getFeature().getFeatureParameters()); - spawns.add(spawnName, thisSpawn); - }); - root.add(ConfigNames.SPAWNS, spawns); - p.write(gson.toJson(root)); - } catch (final IOException e) { - CrashReport report = CrashReport.makeCrashReport(e, - "Failed writing config data " + conf.toString()); - report.getCategory().addCrashSection("OreSpawn Version", Constants.VERSION); - OreSpawn.LOGGER.info(report.getCompleteReport()); - } - - } - - public static void saveSingle(final String fileName) { - final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - saveSingle(Constants.CONFDIR.resolve(fileName), gson); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java new file mode 100644 index 0000000..b31c858 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java @@ -0,0 +1,101 @@ +package com.mcmoddev.orespawn.registries; + +import java.lang.reflect.InvocationTargetException; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS4Feature; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistryModifiable; +import net.minecraftforge.registries.RegistryBuilder; + +public class FeaturesRegistry { + private static final IForgeRegistryModifiable featuresRegistry = (IForgeRegistryModifiable) new RegistryBuilder() + .setName(new ResourceLocation("orespawn", "featuress_registry")) + .allowModification() + .setType(IFeatureEntry.class) + .setMaxID(Integer.MAX_VALUE) + .create(); + + public static final FeaturesRegistry INSTANCE = new FeaturesRegistry(); + + FeaturesRegistry() { + + } + + public void addFeature(final DefaultFeatureEntry feature) { + if ( feature.getClass() == null) return; + featuresRegistry.register(feature); + } + + public void addFeature(final ResourceLocation loc, final String featureClass) { + this.addFeature(new DefaultFeatureEntry(loc, featureClass)); + } + + public void addFeature(final String entryName, final String featureClass) { + String resDom = entryName.contains(":")?entryName.split(":")[0]:"orespawn"; + String resPath = entryName.contains(":")?entryName.split(":")[1]:entryName; + this.addFeature(new ResourceLocation(resDom, resPath), featureClass); + } + + public OS4Feature getFeature(final String featureName) { + IFeatureEntry feature = featuresRegistry.getValue(featureName.contains(":")?new ResourceLocation(featureName):new ResourceLocation("orespawn",featureName)); + try { + return (OS4Feature) feature.getClass().getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { + OreSpawn.LOGGER.fatal(e.getMessage()); + return null; + } + } + + private class DefaultFeatureEntry implements IFeatureEntry { + private ResourceLocation resLoc; + private String featureClassPath; + private Class featureClass; + + @SuppressWarnings("unchecked") + public DefaultFeatureEntry(final ResourceLocation location, final String classPath) { + this.resLoc = location; + this.featureClassPath = classPath; + try { + this.featureClass = (Class)Class.forName(classPath); + } catch(ClassNotFoundException e) { + OreSpawn.LOGGER.error(e.getMessage()); + this.featureClass = null; + } + } + + public Class getFeature() { + return this.featureClass; + } + + public String getClassName() { + return this.featureClassPath; + } + + public void setFeatureName(String name) { + String nameBits[] = name.split(":"); + String resDom = nameBits.length > 0?nameBits[0]:"orespawn"; + String resPath = nameBits.length > 0?nameBits[1]:name; + this.resLoc = new ResourceLocation(resDom, resPath); + } + + @Override + public ResourceLocation getRegistryName() { + return this.resLoc; + } + + @Override + public Class getRegistryType() { + return IFeatureEntry.class; + } + + @Override + public IFeatureEntry setRegistryName(ResourceLocation arg0) { + this.resLoc = arg0; + return this; + } + + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java new file mode 100644 index 0000000..a144ab5 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java @@ -0,0 +1,11 @@ +package com.mcmoddev.orespawn.registries; + +import net.minecraftforge.registries.IForgeRegistryEntry; + +import com.mcmoddev.orespawn.api.OS4Feature; + +public interface IFeatureEntry extends IForgeRegistryEntry { + Class getFeature(); + String getClassName(); + void setFeatureName(String name); +} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java new file mode 100644 index 0000000..6b2f5fb --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java @@ -0,0 +1,10 @@ +package com.mcmoddev.orespawn.registries; + +import com.google.gson.JsonElement; + +import net.minecraftforge.registries.IForgeRegistryEntry; + +public interface IPresetEntry extends IForgeRegistryEntry { + JsonElement getData(); + IPresetEntry setData(JsonElement data); +} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java new file mode 100644 index 0000000..59bd174 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java @@ -0,0 +1,17 @@ +package com.mcmoddev.orespawn.registries; + +import java.util.List; + +import com.mcmoddev.orespawn.data.OS4BlockData; + +import net.minecraft.block.Block; +import net.minecraftforge.common.extensions.IForgeBlockState; +import net.minecraftforge.registries.IForgeRegistryEntry; + +public interface IReplacementEntry extends IForgeRegistryEntry { + List getReplacementData(); + List getReplacementState(); + List getReplacementBlock(); + void setData(List data); + void resolveBlocks(); +} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java new file mode 100644 index 0000000..6618793 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java @@ -0,0 +1,86 @@ +package com.mcmoddev.orespawn.registries; + +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistryModifiable; +import net.minecraftforge.registries.RegistryBuilder; + +public class PresetsRegistry { + + private static final IForgeRegistryModifiable presetsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() + .setName(new ResourceLocation("orespawn", "presets_registry")) + .allowModification() + .setType(IPresetEntry.class) + .setMaxID(Integer.MAX_VALUE) + .create(); + + public static final PresetsRegistry INSTANCE = new PresetsRegistry(); + + PresetsRegistry() { + } + + public Map getPresets() { + return ImmutableMap.copyOf(presetsRegistry.getEntries().stream() + .map( entry -> Map.entry(entry.getValue().getRegistryName(), entry.getValue()) ) + .collect(Collectors.toSet())); + } + + public JsonElement getPreset(String name) { + return (name.contains(":")?presetsRegistry.getValue(new ResourceLocation(name)):presetsRegistry.getValue(new ResourceLocation("orespawn", name))).getData(); + } + + public void addPreset(String name, JsonElement data) { + this.addPreset(new ResourceLocation("orespawn", name), data); + } + + public void addPreset(ResourceLocation rl, JsonElement data) { + this.addPreset(rl, new DefaultEntry(data)); + } + + public void addPreset(ResourceLocation rl, IPresetEntry entry) { + presetsRegistry.register(entry.setRegistryName(rl)); + } + + private class DefaultEntry implements IPresetEntry { + private ResourceLocation name; + private JsonElement presetData; + + DefaultEntry(final JsonElement data) { + this.presetData = data; + } + + @Override + public ResourceLocation getRegistryName() { + return name; + } + + @Override + public Class getRegistryType() { + return IPresetEntry.class; + } + + @Override + public IPresetEntry setRegistryName(ResourceLocation arg0) { + this.name = arg0; + return this; + } + + @Override + public JsonElement getData() { + return presetData==null?new JsonArray():this.presetData; + } + + @Override + public IPresetEntry setData(JsonElement data) { + this.presetData = data; + return this; + } + + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java new file mode 100644 index 0000000..2025a8f --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java @@ -0,0 +1,97 @@ +package com.mcmoddev.orespawn.registries; + +import net.minecraft.block.Block; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.extensions.IForgeBlockState; +import net.minecraftforge.registries.IForgeRegistryModifiable; +import net.minecraftforge.registries.RegistryBuilder; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.data.OS4BlockData; + +public class ReplacementsRegistry { + private static final IForgeRegistryModifiable replacementsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() + .setName(new ResourceLocation("orespawn", "replacements_registry")) + .allowModification() + .setType(IReplacementEntry.class) + .setMaxID(Integer.MAX_VALUE) + .create(); + + public static final ReplacementsRegistry INSTANCE = new ReplacementsRegistry(); + + ReplacementsRegistry() { + + } + + public void addReplacement(final String repName, final List data) { + if(repName.contains(":")) addReplacement(new ResourceLocation(repName), data); + else addReplacement(new ResourceLocation("orespawn", repName), data); + } + + public void addReplacement(final ResourceLocation loc, final List data) { + DefaultReplacementEntry ent = new DefaultReplacementEntry(loc, data); + addReplacement(ent); + } + + public void addReplacement(final IReplacementEntry entry) { + replacementsRegistry.register(entry); + } + + private class DefaultReplacementEntry implements IReplacementEntry { + private ResourceLocation name; + private final List data; + private List blockStates; + + DefaultReplacementEntry(ResourceLocation loc, List blocks) { + this.name = loc; + this.data = blocks; + } + @Override + public ResourceLocation getRegistryName() { + return this.name; + } + + @Override + public Class getRegistryType() { + return IReplacementEntry.class; + } + + @Override + public IReplacementEntry setRegistryName(ResourceLocation arg0) { + this.name = arg0; + return this; + } + + @Override + public List getReplacementData() { + return ImmutableList.copyOf(this.data); + } + + @Override + public List getReplacementState() { + return ImmutableList.copyOf(this.blockStates); + } + + @Override + public List getReplacementBlock() { + return ImmutableList.copyOf(this.blockStates.stream() + .map(bls -> bls.getBlockState().getBlock()).collect(Collectors.toList())); + } + + @Override + public void setData(List data) { + Collections.copy(this.data, data); + } + + @Override + public void resolveBlocks() { + // TODO: Complete - should iterate this.data and use the state deserialize routines + // dereferencing from the blocks list + } + + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/util/Collectors2.java b/src/main/java/com/mcmoddev/orespawn/util/Collectors2.java deleted file mode 100644 index bbe8ade..0000000 --- a/src/main/java/com/mcmoddev/orespawn/util/Collectors2.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mcmoddev.orespawn.util; - -import java.util.stream.Collector; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; - -public final class Collectors2 { - - private Collectors2() { - } - - public static Collector, ImmutableList> toImmutableList() { - return Collector.of(ImmutableList.Builder::new, ImmutableList.Builder::add, - (left, right) -> left.addAll(right.build()), ImmutableList.Builder::build); - } - - public static Collector, ImmutableSet> toImmutableSet() { - return Collector.of(ImmutableSet.Builder::new, ImmutableSet.Builder::add, - (left, right) -> left.addAll(right.build()), ImmutableSet.Builder::build); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/util/StateUtil.java b/src/main/java/com/mcmoddev/orespawn/util/StateUtil.java deleted file mode 100644 index 1997dae..0000000 --- a/src/main/java/com/mcmoddev/orespawn/util/StateUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.mcmoddev.orespawn.util; - -import com.google.common.base.Optional; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.exceptions.BadStateValueException; - -import net.minecraft.block.Block; -import net.minecraft.block.properties.IProperty; -import net.minecraft.block.state.IBlockState; - -public class StateUtil { - - private StateUtil() { - throw new InstantiationError("This class cannot be instantiated!"); - } - - public static String serializeState(final IBlockState state) { - String string = state.toString(); - string = string.substring(string.indexOf('[') + 1, - string.length() - (string.endsWith("]") ? 1 : 0)); - - if (string.equals(state.getBlock().getRegistryName().toString())) { - string = "normal"; - } - - OreSpawn.LOGGER.debug("State is %s (for block %s)", string, - state.getBlock().getRegistryName()); - return string; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static IBlockState deserializeState(final Block block, final String state) throws BadStateValueException { - String bits[]; - if(state.contains(",")) bits = state.split(","); - else bits = new String[] { state }; - - IBlockState rv = block.getDefaultState(); - - for(String sv : bits) { - String kvp[] = sv.split("="); - IProperty prop = block.getBlockState().getProperty(kvp[0]); - if(prop != null) { - Optional propValue = prop.parseValue(kvp[1]); - if(propValue.isPresent()) - rv = rv.withProperty(prop, propValue.get()); - else - throw new BadStateValueException(String.format("%s is not a valid value for property %s", kvp[1], kvp[0])); - } else { - throw new BadStateValueException(String.format("%s is not a known property of %s", kvp[0], block.getRegistryName())); - } - } - - return rv; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java new file mode 100644 index 0000000..45b2142 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -0,0 +1,7 @@ +package com.mcmoddev.orespawn.utils; + +public class Helpers { + public static boolean objectNotNull(Object obj) { + return obj != null; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java b/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java new file mode 100644 index 0000000..2488b6b --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java @@ -0,0 +1,16 @@ +package com.mcmoddev.orespawn.utils; + +import org.objectweb.asm.Type; + +import com.mcmoddev.orespawn.OreSpawn; + +public class TypeUtils { + public static Class getClassFromType(Type type) { + try { + return Class.forName(type.getClassName(), false, OreSpawn.class.getClassLoader()); + } catch(ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/worldgen/FlatBedrock.java b/src/main/java/com/mcmoddev/orespawn/worldgen/FlatBedrock.java deleted file mode 100644 index 497069e..0000000 --- a/src/main/java/com/mcmoddev/orespawn/worldgen/FlatBedrock.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.mcmoddev.orespawn.worldgen; - -import java.util.Random; - -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.WorldType; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.fml.common.IWorldGenerator; - -public class FlatBedrock implements IWorldGenerator { - - @Override - public void generate(final Random random, final int chunkX, final int chunkZ, final World world, - final IChunkGenerator chunkGenerator, final IChunkProvider chunkProvider) { - // no need to do flat-bedrock on a "FLAT" world - if (world.getWorldType() != WorldType.FLAT) { - if (world.provider.getDimension() == -1) { - genTopPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.NETHERRACK); - genBottomPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.NETHERRACK); - } else if (world.provider.getDimension() >= 0 && world.provider.getDimension() != 1) { - genBottomPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.STONE); - } - } - } - - public void retrogen(final World world, final int chunkX, final int chunkZ) { - if (world.getWorldType() != WorldType.FLAT) { - if (world.provider.getDimension() == -1) { - genTopPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.NETHERRACK); - genBottomPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.NETHERRACK); - } else if (world.provider.getDimension() >= 0 && world.provider.getDimension() != 1) { - genBottomPlate(world, new ChunkPos(chunkX, chunkZ), Blocks.STONE); - } - } - } - - private void genBottomPlate(final World world, final ChunkPos chunkPos, final Block repBlock) { - final int plateThickness = Config.getInt(Constants.BEDROCK_LAYERS); - - for (int xP = 0; xP < 16; xP++) { - for (int zP = 0; zP < 16; zP++) { - for (int yP = 5; yP > 0; yP--) { - final BlockPos target = new BlockPos(chunkPos.x * 16 + xP, yP, - chunkPos.z * 16 + zP); - - if (yP < plateThickness - && !world.getBlockState(target).getBlock().equals(Blocks.BEDROCK)) { - world.setBlockState(target, Blocks.BEDROCK.getDefaultState(), 26); - } else if (yP >= plateThickness - && world.getBlockState(target).getBlock().equals(Blocks.BEDROCK)) { - world.setBlockState(target, repBlock.getDefaultState(), 26); - } - } - } - } - } - - private void genTopPlate(final World world, final ChunkPos chunkPos, final Block repBlock) { - final int plateThickness = Config.getInt(Constants.BEDROCK_LAYERS); - final int thickness = 127 - plateThickness; // layer where the flat for the top starts - - for (int xP = 0; xP < 16; xP++) { - for (int zP = 0; zP < 16; zP++) { - for (int yP = 126; yP > 121; yP--) { - final BlockPos target = new BlockPos(chunkPos.x * 16 + xP, yP, - chunkPos.z * 16 + zP); - - if (yP > thickness - && !world.getBlockState(target).getBlock().equals(Blocks.BEDROCK)) { - world.setBlockState(target, Blocks.BEDROCK.getDefaultState(), 26); - } else if (yP <= thickness - && world.getBlockState(target).getBlock().equals(Blocks.BEDROCK)) { - world.setBlockState(target, repBlock.getDefaultState(), 26); - } - } - } - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnFeatureGenerator.java b/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnFeatureGenerator.java deleted file mode 100644 index a650109..0000000 --- a/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnFeatureGenerator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mcmoddev.orespawn.worldgen; - -import java.util.Random; - -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.api.os3.OS3FeatureGenerator; -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.fml.common.IWorldGenerator; -import net.minecraft.util.math.ChunkPos; - -public class OreSpawnFeatureGenerator implements IWorldGenerator, OS3FeatureGenerator { - private final ISpawnEntry spawn; - private final String name; - - public OreSpawnFeatureGenerator( final ISpawnEntry spawn, final String name ) { - this.spawn = spawn; - this.name = name; - } - - @Override - public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, - IChunkProvider chunkProvider) { - final int thisDim = world.provider.getDimension(); - - if ((Config.getBoolean(Constants.RETROGEN_KEY) && - ((this.spawn.isRetrogen() || Config.getBoolean(Constants.FORCE_RETROGEN_KEY)))) || - (this.spawn.isEnabled() && this.spawn.dimensionAllowed(thisDim))) { - this.spawn.generate(random, world, chunkGenerator, chunkProvider, new ChunkPos(chunkX, chunkZ)); - } - } - - @Override - public ISpawnEntry getSpawnData() { - return this.spawn; - } - - @Override - public String getSpawnName() { - return this.name; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnWorldGen.java b/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnWorldGen.java deleted file mode 100644 index 94d5e05..0000000 --- a/src/main/java/com/mcmoddev/orespawn/worldgen/OreSpawnWorldGen.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.mcmoddev.orespawn.worldgen; - -import java.util.Random; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.os3.ISpawnEntry; -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.IChunkGenerator; -import net.minecraftforge.fml.common.IWorldGenerator; - -public class OreSpawnWorldGen implements IWorldGenerator { - - @Override - public void generate(final Random random, final int chunkX, final int chunkZ, final World world, - final IChunkGenerator chunkGenerator, final IChunkProvider chunkProvider) { - - final int thisDim = world.provider.getDimension(); - - OreSpawn.API.getSpawns(thisDim).stream().filter(ISpawnEntry::isEnabled) - .filter(sb -> !Config.getBoolean(Constants.RETROGEN_KEY) - || (sb.isRetrogen() || Config.getBoolean(Constants.FORCE_RETROGEN_KEY))) - .forEach(spawn -> spawn.generate(random, world, chunkGenerator, chunkProvider, - new ChunkPos(chunkX, chunkZ))); - } -} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..e87f17a --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,56 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory +# A version range to match for said mod loader - for regular FML @Mod it will be the forge version +loaderVersion="[25,)" #mandatory (24 is current forge version) +# A URL to refer people to when problems occur with this mod +issueTrackerURL="http://my.issue.tracker/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId="examplemod" #mandatory +# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it +version="${file.jarVersion}" #mandatory + # A display name for the mod +displayName="Example Mod" #mandatory +# A URL to query for updates for this mod. See the JSON update specification +updateJSONURL="http://myurl.me/" #optional +# A URL for the "homepage" for this mod, displayed in the mod UI +displayURL="http://example.com/" #optional +# A file name (in the root of the mod JAR) containing a logo for display +logoFile="examplemod.png" #optional +# A text field displayed in the mod UI +credits="Thanks for this example mod goes to Java" #optional +# A text field displayed in the mod UI +authors="Love, Cheese and small house plants" #optional +# The description text for the mod (multi line!) (#mandatory) +description=''' +This is a long form description of the mod. You can write whatever you want here + +Have some lorem ipsum. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. +''' +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.examplemod]] #optional + # the modid of the dependency + modId="forge" #mandatory + # Does this dependency have to exist - if not, ordering below must be specified + mandatory=true #mandatory + # The version range of the dependency + versionRange="[25,)" #mandatory + # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side="BOTH" +# Here's another dependency +[[dependencies.examplemod]] + modId="minecraft" + mandatory=true + versionRange="[1.13.2]" + ordering="NONE" + side="BOTH" \ No newline at end of file diff --git a/src/main/resources/assets/orespawn/configs/_features.json b/src/main/resources/assets/orespawn/configs/_features.json deleted file mode 100644 index 09075a3..0000000 --- a/src/main/resources/assets/orespawn/configs/_features.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "name": "default", - "class": "com.mcmoddev.orespawn.impl.features.DefaultFeatureGenerator" - }, - { - "name": "vein", - "class": "com.mcmoddev.orespawn.impl.features.VeinGenerator" - }, - { - "name": "normal-cloud", - "class": "com.mcmoddev.orespawn.impl.features.NormalCloudGenerator" - }, - { - "name": "precision", - "class": "com.mcmoddev.orespawn.impl.features.PrecisionGenerator" - }, - { - "name": "clusters", - "class": "com.mcmoddev.orespawn.impl.features.ClusterGenerator" - }, - { - "name": "underfluids", - "class": "com.mcmoddev.orespawn.impl.features.UnderFluid" - } -] \ No newline at end of file diff --git a/src/main/resources/assets/orespawn/configs/_replacements.json b/src/main/resources/assets/orespawn/configs/_replacements.json deleted file mode 100644 index 8e378cf..0000000 --- a/src/main/resources/assets/orespawn/configs/_replacements.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "default": [ { "name": "minecraft:stone" }, - { "name": "minecraft:andesite" }, - { "name": "minecraft:diorite" }, - { "name": "minecraft:granite" }, - { "name": "minecraft:netherrack" }, - { "name": "minecraft:end_stone" } ] -} diff --git a/src/main/resources/assets/orespawn/configs/orespawn.json b/src/main/resources/assets/orespawn/configs/orespawn.json deleted file mode 100755 index 4021c4f..0000000 --- a/src/main/resources/assets/orespawn/configs/orespawn.json +++ /dev/null @@ -1,332 +0,0 @@ -{ - "version": "2.0", - "spawns": { - "quartz_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [ - -1 - ], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 15, - "variation": 4, - "frequency": 7, - "minHeight": 0, - "maxHeight": 128 - }, - "blocks": [ - { - "name": "minecraft:quartz_ore", - "chance": 100 - } - ] - }, - "coal_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 25, - "variation": 12, - "frequency": 20, - "minHeight": 0, - "maxHeight": 128 - }, - "blocks": [ - { - "name": "minecraft:coal_ore", - "chance": 100 - } - ] - }, - "iron_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 8, - "variation": 4, - "frequency": 20, - "minHeight": 0, - "maxHeight": 64 - }, - "blocks": [ - { - "name": "minecraft:iron_ore", - "chance": 100 - } - ] - }, - "gold_ore_standard": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 8, - "variation": 2, - "frequency": 2, - "minHeight": 0, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:gold_ore", - "chance": 100 - } - ] - }, - "gold_ore_mesa": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "includes": [ "MESA" ] - }, - "parameters": { - "size": 8, - "variation": 2, - "frequency": 2, - "minHeight": 32, - "maxHeight": 79 - }, - "blocks": [ - { - "name": "minecraft:gold_ore", - "chance": 100 - } - ] - }, - "diamond_ore": { - "retrogen": false, - "enabled": true, - "feature": "precision", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 3, - "numObjects": 2, - "minHeight": 0, - "maxHeight": 16 - }, - "blocks": [ - { - "name": "minecraft:diamond_ore", - "chance": 100 - } - ] - }, - "redstone_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 6, - "variation": 3, - "frequency": 8, - "minHeight": 0, - "maxHeight": 16 - }, - "blocks": [ - { - "name": "minecraft:redstone_ore", - "chance": 100 - } - ] - }, - "lapis_ore": { - "retrogen": false, - "enabled": false, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 5, - "variation": 2, - "frequency": 1, - "minHeight": 0, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:lapis_ore", - "chance": 100 - } - ] - }, - "emerald_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "includes": [ "MOUNTAIN" ] - }, - "parameters": { - "size": 1, - "variation": 0, - "frequency": 8, - "minHeight": 4, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:emerald_ore", - "chance": 100 - } - ] - }, - "dirt": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:dirt", - "chance": 100, - "state": "snowy=false,variant=dirt" - } - ] - }, - "gravel": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 8, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:gravel", - "chance": 100 - } - ] - }, - "andesite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=andesite" - } - ] - }, - "diorite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=diorite" - } - ] - }, - "granite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=granite" - } - ] - } - } -} \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index c9bf053..0000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "modid": "orespawn", - "name": "OreSpawn", - "description": "A customizable ore-spawning system that uses JSON files to specify custom ore generation", - "version": "${version}", - "authorList": [ - "dshadowwolf", - "iLexiconn", - "jriwanek", - "DrCyano" - ], - "credits": "The MMD community" - } -] diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 6065f6d..0bd71cb 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,7 @@ { - "pack":{ - "pack_format":2, - "description":"Ore Spawn" + "pack": { + "description": "examplemod resources", + "pack_format": 4, + "_comment": "A pack_format of 4 requires json lang files. Note: we require v4 pack meta for all mods." } } diff --git a/update.json b/update.json new file mode 100644 index 0000000..805cf5b --- /dev/null +++ b/update.json @@ -0,0 +1,80 @@ +{ + "homepage": "http://minecraft.curseforge.com/projects/examplemod", + "promos": { + "1.12.2-latest": "1.0.0", + "1.12.1-latest": "1.0.0", + "1.12-latest": "1.0.0", + "1.11.2-latest": "0.0.0", + "1.11-latest": "0.0.0", + "1.10.2-latest": "0.0.0", + "1.10-latest": "0.0.0", + "1.9.4-latest": "0.0.0", + "1.9-latest": "0.0.0", + "1.8.9-latest": "0.0.0", + "1.8.8-latest": "0.0.0", + "1.8-latest": "0.0.0", + "1.7.10-latest": "0.0.0", + "1.7.2-latest": "0.0.0", + "1.6.4-latest": "0.0.0", + "1.12.2-recommended": "1.0.0", + "1.12.1-recommended": "1.0.0", + "1.12-recommended": "1.0.0", + "1.11.2-recommended": "0.0.0", + "1.11-recommended": "0.0.0", + "1.10.2-recommended": "0.0.0", + "1.10-recommended": "0.0.0", + "1.9.4-recommended": "0.0.0", + "1.9-recommended": "0.0.0", + "1.8.9-recommended": "0.0.0", + "1.8.8-recommended": "0.0.0", + "1.8-recommended": "0.0.0", + "1.7.10-recommended": "0.0.0", + "1.7.2-recommended": "0.0.0", + "1.6.4-recommended": "0.0.0" + }, + "1.12.2": { + "1.0.0": "Working Basemod, Block and Item Examples" + }, + "1.12.1": { + "1.0.0": "Working Basemod, Block and Item Examples" + }, + "1.12": { + "1.0.0": "Working Basemod, Block and Item Examples" + }, + "1.11.2": { + "0.0.0": "No Release" + }, + "1.11": { + "0.0.0": "No Release" + }, + "1.10.2": { + "0.0.0": "No Release" + }, + "1.10": { + "0.0.0": "No Release" + }, + "1.9.4": { + "0.0.0": "No Release" + }, + "1.9": { + "0.0.0": "No Release" + }, + "1.8.9": { + "0.0.0": "No Release" + }, + "1.8.8": { + "0.0.0": "No Release" + }, + "1.8": { + "0.0.0": "No Release" + }, + "1.7.10": { + "0.0.0": "No Release" + }, + "1.7.2": { + "0.0.0": "No Release" + }, + "1.6.4": { + "0.0.0": "No Release" + } +} From b21df899b4b12434953b37deed78c2ecedb9b594 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sat, 3 Jul 2021 19:08:19 -0400 Subject: [PATCH 02/37] A lot of work... 1) Removal of the entire plugin/annotation system coming - first start is in the code right now 2) Change to doing a flat resource scan for files in an `orespawn4-data` directory under `assets` 3) Add collection of files on disk into the mix there 4) Mostly stubbed out buidlers for various bits 5) Untested but largely complete loaders for things 6) Cleanups and fixes to issues with the `mods.toml` --- gradle.properties | 2 +- .../java/com/mcmoddev/orespawn/OreSpawn.java | 145 +++++--- .../orespawn/api/{OS3API.java => OS4API.java} | 74 +++- .../api/{os3plugin.java => OS4Plugin.java} | 2 +- .../orespawn/api/plugin/PluginLoader.java | 12 +- .../orespawn/builders/BiomeMatcher.java | 11 + .../orespawn/builders/DimensionMatcher.java | 11 + .../orespawn/builders/SpawnBuilder.java | 122 +++++++ .../com/mcmoddev/orespawn/data/Constants.java | 13 + .../mcmoddev/orespawn/data/OS4BlockData.java | 7 + .../orespawn/registries/PresetsRegistry.java | 11 - .../orespawn/registries/SpawnStore.java | 10 + src/main/resources/META-INF/mods.toml | 57 +-- .../orespawn4-data/features/features.json | 26 ++ .../assets/orespawn4-data/orespawn.json | 332 ++++++++++++++++++ .../replacements/replacements.json | 8 + 16 files changed, 713 insertions(+), 130 deletions(-) rename src/main/java/com/mcmoddev/orespawn/api/{OS3API.java => OS4API.java} (67%) rename src/main/java/com/mcmoddev/orespawn/api/{os3plugin.java => OS4Plugin.java} (95%) create mode 100644 src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java create mode 100644 src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java create mode 100644 src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java create mode 100644 src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java create mode 100644 src/main/resources/assets/orespawn4-data/features/features.json create mode 100644 src/main/resources/assets/orespawn4-data/orespawn.json create mode 100644 src/main/resources/assets/orespawn4-data/replacements/replacements.json diff --git a/gradle.properties b/gradle.properties index 4213929..873404e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.daemon = false - + org.gradle.jvmargs=-Xmx6144M group = com.mcmoddev archivesBaseName = OreSpawn modid = orespawn diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 1da0d94..7b0921d 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,32 +1,32 @@ package com.mcmoddev.orespawn; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; +import net.minecraft.util.ResourceLocation; 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.ModList; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; -import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.forgespi.language.ModFileScanData; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Type; -import com.mcmoddev.orespawn.api.OS3API; -import com.mcmoddev.orespawn.api.os3plugin; -import com.mcmoddev.orespawn.api.plugin.PluginLoader; -import com.mcmoddev.orespawn.utils.Helpers; -import com.mcmoddev.orespawn.utils.TypeUtils; +import com.mcmoddev.orespawn.data.Constants; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.LinkedList; import java.util.stream.Collectors; +import java.util.List; +import java.util.Locale; + +import com.google.common.base.Joiner; @Mod("orespawn") public class OreSpawn { @@ -47,60 +47,93 @@ public OreSpawn() { MinecraftForge.EVENT_BUS.register(this); } + private List iterateFiles(ModFile modFile) { + try { + Path root = modFile.getLocator().findPath(modFile, "assets", "orespawn4-data").toAbsolutePath(); + + return Files.walk(root). + map(path -> root.relativize(path.toAbsolutePath())). + filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds + filter(path -> path.toString().endsWith(".json")). + map(path -> Joiner.on('/').join(path)). + map(path -> path.toString()). + map(path -> path.substring(0, path.length() - 5)). + map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)). + collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } + + List iterateDiskFiles() { + try { + Path diskPath = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4).toAbsolutePath(); + return Files.walk(diskPath) + .map(path -> diskPath.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64) + .filter(path -> path.toString().endsWith(".json")) + .map(path -> Joiner.on('/').join(path)) + .map(path -> path.toString()) + .map(path -> path.toLowerCase(Locale.US)) + .map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation("orespawn-disk", path)) + .collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } private void setup(final FMLCommonSetupEvent event) { - final Type annotationType = Type.getType(os3plugin.class); - ModList.get().getAllScanData().stream() - .flatMap( scanData -> scanData.getAnnotations() - .stream() - .filter(a -> annotationType.equals(a.getAnnotationType()))) - .map(ModFileScanData.AnnotationData::getClassType) - .map(TypeUtils::getClassFromType) - .filter(Helpers::objectNotNull) - .forEach(PluginLoader::Load); - // load configs - OS3API.loadPresets(); - OS3API.loadReplacements(); - OS3API.loadFeatures(); - OS3API.loadIntegrationWhitelist(); - OS3API.loadSpawns(); + List foundFiles = new LinkedList<>(); + ModList.get().getModFiles().stream() + .map(mfi -> iterateFiles(mfi.getFile())) + .forEach(lrl -> lrl.stream().forEach(rl -> foundFiles.add(rl))); + + iterateDiskFiles().stream().forEach(rl -> foundFiles.add(rl)); + + List featuresFiles = foundFiles.stream() + .filter(rl -> rl.getPath().startsWith("features/")) + .collect(Collectors.toList()); + List replacementsFiles = foundFiles.stream() + .filter(rl -> rl.getPath().startsWith("replacements/")) + .collect(Collectors.toList()); + List presetsFiles = foundFiles.stream() + .filter(rl -> rl.getPath().startsWith("presets/")) + .collect(Collectors.toList()); + List spawnConfigs = foundFiles.stream() + .filter(rl -> !featuresFiles.contains(rl)) + .filter(rl -> !replacementsFiles.contains(rl)) + .filter(rl -> !presetsFiles.contains(rl)) + .collect(Collectors.toList()); + + LOGGER.info("Found {} features files, {} replacements files, {} presets files and {} spawn configuration files", + featuresFiles.size(), replacementsFiles.size(), presetsFiles.size(), spawnConfigs.size()); + LOGGER.info("> Features Files:"); + featuresFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); + LOGGER.info("> Replacements Files:"); + replacementsFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); + LOGGER.info("> Presets Files:"); + presetsFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); + LOGGER.info("> Spawn Configs:"); + spawnConfigs.forEach(rl -> LOGGER.info(">> {}", rl.toString())); + + // load configs + /* + OS4API.loadPresets(); + OS4API.loadReplacements(); + OS4API.loadFeatures(); + OS4API.loadIntegrationWhitelist(); + OS4API.loadSpawns(); + */ // register world gen } private void doClientStuff(final FMLClientSetupEvent event) { - // do something that can only be done on the client - LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings); } private void enqueueIMC(final InterModEnqueueEvent event) { - // some example code to dispatch IMC to another mod - InterModComms.sendTo("orespawn", "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.getMessageSupplier().get()). - collect(Collectors.toList())); - } - - // You can use SubscribeEvent and let the Event Bus discover methods to call - @SubscribeEvent - public void onServerStarting(final 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/java/com/mcmoddev/orespawn/api/OS3API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java similarity index 67% rename from src/main/java/com/mcmoddev/orespawn/api/OS3API.java rename to src/main/java/com/mcmoddev/orespawn/api/OS4API.java index f7b9c9d..aa4f8fa 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS3API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -6,6 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -21,13 +22,15 @@ import com.google.gson.JsonParser; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.builders.SpawnBuilder; import com.mcmoddev.orespawn.data.Constants; import com.mcmoddev.orespawn.data.OS4BlockData; import com.mcmoddev.orespawn.registries.FeaturesRegistry; import com.mcmoddev.orespawn.registries.PresetsRegistry; import com.mcmoddev.orespawn.registries.ReplacementsRegistry; +import com.mcmoddev.orespawn.registries.SpawnStore; -public class OS3API { +public class OS4API { private static Map modRefs = new HashMap<>(1024); private static List allowedConfigs = new LinkedList<>(); @@ -38,7 +41,10 @@ public static void addMod(final String modId, final String modPath) { public static Map getMods() { return Collections.unmodifiableMap(modRefs); } - + + /* + * Walk the config directory, load all the various `presets-XXX.json` files + */ public static void loadPresets() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/presets-*.json"); @@ -77,6 +83,9 @@ public static void loadPresets() { } } + /* + * Walk the config directory, load all the various `replacements-XXX.json` files + */ public static void loadReplacements() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/replacements-*.json"); @@ -100,9 +109,6 @@ public static void loadReplacements() { elements.entrySet().stream() .forEach( repl -> { String replName = repl.getKey(); - if (repl.getValue().isJsonPrimitive()) { - // TODO: Preset Shit - } else { JsonArray replValues = repl.getValue().getAsJsonArray(); List blocks = new LinkedList(); for( JsonElement el : replValues) { @@ -112,7 +118,6 @@ public static void loadReplacements() { blocks.add(new OS4BlockData(blName, state)); } ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); - } }); }); } catch (final IOException e) { @@ -120,6 +125,9 @@ public static void loadReplacements() { } } + /* + * Walk the config directory, load all the various `features-XXX.json` files + */ public static void loadFeatures() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/features-*.json"); @@ -152,6 +160,11 @@ public static void loadFeatures() { } } + /* + * Load the `Active Configs` file + * This one needs a bit more thought, right now its just a list of filenames... + * It might be better to have some structure so mods themselves can be listed + */ public static void loadIntegrationWhitelist() { Path p = Constants.SYSCONF.resolve(Constants.FileBits.ALLOWED_MODS); final JsonParser parser = new JsonParser(); @@ -167,11 +180,44 @@ public static void loadIntegrationWhitelist() { parser.parse(rawJson).getAsJsonArray().forEach( item -> allowedConfigs.add(item.getAsString())); } - public static void loadSpawns() { - // find all JSON files that are not in `sysconf` - // filter for only active-flagged files - // load them, interpolating any presets present - // stuff them into a registry - // return - } -} + public static void loadSpawns() { + final JsonParser parser = new JsonParser(); + + Arrays.asList(Constants.JSONPATH.toFile().listFiles()) + .stream() + // find all JSON files + .filter( f -> f.toPath().endsWith(".json")) + // that are not in SYSCONF + .filter( f -> !f.getAbsolutePath().contains(Constants.FileBits.SYSCONF)) + // and are listed in allowedConfigs + .filter( f -> allowedConfigs.contains(f.getName().substring(0, f.getName().length() - 5))) + .forEach( inFile -> { + // try to read the file + String rawData; + try { + rawData = FileUtils.readFileToString(inFile, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(String.format("Cannot load %s:\n%s", inFile.getName(), e.getMessage())); + return; + } + + // parse the file + JsonObject rawJson = parser.parse(rawData).getAsJsonObject(); + + String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); + JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); + + OreSpawn.LOGGER.info("Loading JSON version {} from {}", fileVersion, inFile.getName()); + + // load the spawns + spawnWrapper.entrySet().stream() + .forEach( entry -> { + String spawnName = entry.getKey(); + JsonObject spawnData = entry.getValue().getAsJsonObject(); + SpawnBuilder theBuilder = new SpawnBuilder(spawnName); + spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); + SpawnStore.add(spawnName, theBuilder.build()); + }); + }); + } + } diff --git a/src/main/java/com/mcmoddev/orespawn/api/os3plugin.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java similarity index 95% rename from src/main/java/com/mcmoddev/orespawn/api/os3plugin.java rename to src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java index c0be996..c9ceb17 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/os3plugin.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java @@ -8,7 +8,7 @@ @Retention(RUNTIME) @Target(TYPE) -public @interface os3plugin { +public @interface OS4Plugin { // the Mod this is for - will be used for // generating the name of the json the config diff --git a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java b/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java index b6af922..174e904 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java +++ b/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java @@ -20,8 +20,8 @@ import org.apache.commons.io.IOUtils; import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS3API; -import com.mcmoddev.orespawn.api.os3plugin; +import com.mcmoddev.orespawn.api.OS4API; +import com.mcmoddev.orespawn.api.OS4Plugin; import com.mcmoddev.orespawn.data.Config; import com.mcmoddev.orespawn.data.Constants; @@ -29,18 +29,18 @@ public enum PluginLoader { INSTANCE; public static void Load(Class clazz) { - if( clazz.isAnnotationPresent(os3plugin.class) ) { + if( clazz.isAnnotationPresent(OS4Plugin.class) ) { loadInternal(clazz); } } private static void loadInternal(Class cls) { - final os3plugin annot = cls.getAnnotation(os3plugin.class); - OS3API.addMod(annot.modid(), annot.resourcePath()); + final OS4Plugin annot = cls.getAnnotation(OS4Plugin.class); + OS4API.addMod(annot.modid(), annot.resourcePath()); } public static void findResources() { - OS3API.getMods().entrySet() + OS4API.getMods().entrySet() .forEach( ent -> { final String base = String.format(Locale.ENGLISH, "assets/%s/%s", ent.getKey(), ent.getValue()); final URL resURL = INSTANCE.getClass().getClassLoader().getResource(base); diff --git a/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java new file mode 100644 index 0000000..c9e5c4d --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java @@ -0,0 +1,11 @@ +package com.mcmoddev.orespawn.builders; + +import com.google.gson.JsonObject; + +public class BiomeMatcher { + + public BiomeMatcher(JsonObject asJsonObject) { + // TODO Auto-generated constructor stub + } + +} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java new file mode 100644 index 0000000..31f713f --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java @@ -0,0 +1,11 @@ +package com.mcmoddev.orespawn.builders; + +import com.google.gson.JsonObject; + +public class DimensionMatcher { + + public DimensionMatcher(JsonObject asJsonObject) { + // TODO Auto-generated constructor stub + } + +} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java new file mode 100644 index 0000000..faa0515 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java @@ -0,0 +1,122 @@ +package com.mcmoddev.orespawn.builders; + +import com.mcmoddev.orespawn.registries.IReplacementEntry; +import com.mcmoddev.orespawn.registries.PresetsRegistry; + +import java.util.List; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.OS4BlockData; +import com.mcmoddev.orespawn.data.Constants; + +public class SpawnBuilder { + private String myName; + private boolean doRetro; + private boolean active; + private DimensionMatcher dimensions; + private IReplacementEntry replacements; + private BiomeMatcher biomes; + private List blocks; + private JsonObject parameters; + + public SpawnBuilder(final String spawnName) { + myName = spawnName; + } + + public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { + JsonElement actualData = itemData; + if (itemData.isJsonPrimitive()) { + if (itemData.getAsString().startsWith("$")) { + // its a preset, lets mangle it in... + actualData = PresetsRegistry.INSTANCE.getPreset(itemData.getAsString().substring(1)); + } + } + + switch(itemName) { + case Constants.ConfigNames.REPLACEMENT: + this.setReplacement(actualData); + break; + case Constants.ConfigNames.FEATURE: + this.setFeature(actualData); + break; + case Constants.ConfigNames.DIMENSIONS: + this.setDimensions(new DimensionMatcher(actualData.getAsJsonObject())); + break; + case Constants.ConfigNames.BIOMES: + this.setBiomes(new BiomeMatcher(actualData.getAsJsonObject())); + break; + case Constants.ConfigNames.BLOCKS: + this.setBlocks(OS4BlockData.parseJsonData(actualData)); + break; + case Constants.ConfigNames.PARAMETERS: + this.parameters = actualData.getAsJsonObject(); + break; + default: + OreSpawn.LOGGER.error("Unknown config item {} - skipping", itemName); + } + + return this; + } + + private void setBlocks(Object parseJsonData) { + // TODO Auto-generated method stub + + } + + private void setBiomes(BiomeMatcher biomeMatcher) { + // TODO Auto-generated method stub + + } + + private void setDimensions(DimensionMatcher dimensionMatcher) { + // TODO Auto-generated method stub + + } + + private void setReplacement(final JsonElement data) { + + } + + private void setFeature(final JsonElement data) { + + } + + public JsonObject getParameters() { + return parameters; + } + + public List getBlocks() { + return blocks; + } + + public String getMyName() { + return myName; + } + + public boolean isActive() { + return active; + } + + public boolean isRetrogen() { + return doRetro; + } + + public BiomeMatcher getBiomes() { + return biomes; + } + + public IReplacementEntry getReplacements() { + return replacements; + } + + public DimensionMatcher getDimensions() { + return dimensions; + } + + public Object build() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index 5b5ebeb..5b9e1a6 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -7,6 +7,19 @@ public class Constants { public static final String VERSION = "4.0.0"; public static final String CRASH_SECTION = "OreSpawn Version"; public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4, FileBits.SYSCONF); + public static final Path JSONPATH = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4); + public static class ConfigNames { + + public static final String REPLACEMENT = "replacements"; + public static final String FEATURE = "feature"; + public static final String DIMENSIONS = "dimensions"; + public static final String BIOMES = "biomes"; + public static final String BLOCKS = "blocks"; + public static final String PARAMETERS = "parameters"; + public static final String VERSION = "version"; + public static final String SPAWNS = "spawns"; + + } public static class FileBits { public static final String CONFIG_DIR = "config"; public static final String OS4 = "mmd-orespawn-4"; diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java index 7a26096..679904a 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java @@ -1,5 +1,7 @@ package com.mcmoddev.orespawn.data; +import com.google.gson.JsonElement; + public class OS4BlockData { private final String blockIdentifier; private final String blockState; @@ -21,4 +23,9 @@ public final String getBlockName() { public final String getBlockState() { return this.blockState; } + + public static Object parseJsonData(JsonElement itemData) { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java index 6618793..28b7492 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java @@ -1,9 +1,5 @@ package com.mcmoddev.orespawn.registries; -import java.util.Map; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -12,7 +8,6 @@ import net.minecraftforge.registries.RegistryBuilder; public class PresetsRegistry { - private static final IForgeRegistryModifiable presetsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() .setName(new ResourceLocation("orespawn", "presets_registry")) .allowModification() @@ -25,12 +20,6 @@ public class PresetsRegistry { PresetsRegistry() { } - public Map getPresets() { - return ImmutableMap.copyOf(presetsRegistry.getEntries().stream() - .map( entry -> Map.entry(entry.getValue().getRegistryName(), entry.getValue()) ) - .collect(Collectors.toSet())); - } - public JsonElement getPreset(String name) { return (name.contains(":")?presetsRegistry.getValue(new ResourceLocation(name)):presetsRegistry.getValue(new ResourceLocation("orespawn", name))).getData(); } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java new file mode 100644 index 0000000..dce9c83 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java @@ -0,0 +1,10 @@ +package com.mcmoddev.orespawn.registries; + +public class SpawnStore { + + public static void add(String spawnName, Object build) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index e87f17a..aa7c04a 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,56 +1,31 @@ -# This is an example mods.toml file. It contains the data relating to the loading mods. -# There are several mandatory fields (#mandatory), and many more that are optional (#optional). -# The overall format is standard TOML format, v0.5.0. -# Note that there are a couple of TOML lists in this file. -# Find more information on toml format here: https://github.com/toml-lang/toml -# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory -# A version range to match for said mod loader - for regular FML @Mod it will be the forge version -loaderVersion="[25,)" #mandatory (24 is current forge version) -# A URL to refer people to when problems occur with this mod -issueTrackerURL="http://my.issue.tracker/" #optional -# A list of mods - how many allowed here is determined by the individual mod loader +loaderVersion="[36,)" #mandatory (36 is current forge version) +issueTrackerURL="https://github.com/MinecraftModDevelopmentMods/OreSpawn/issues/" #optional +# GNU LGPLv2.1 +license="GNU LGPLv2.1" [[mods]] #mandatory -# The modid of the mod -modId="examplemod" #mandatory -# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it +modId="orespawn" #mandatory version="${file.jarVersion}" #mandatory - # A display name for the mod -displayName="Example Mod" #mandatory -# A URL to query for updates for this mod. See the JSON update specification -updateJSONURL="http://myurl.me/" #optional -# A URL for the "homepage" for this mod, displayed in the mod UI -displayURL="http://example.com/" #optional -# A file name (in the root of the mod JAR) containing a logo for display -logoFile="examplemod.png" #optional -# A text field displayed in the mod UI -credits="Thanks for this example mod goes to Java" #optional -# A text field displayed in the mod UI -authors="Love, Cheese and small house plants" #optional +displayName="MMD OreSpawn" #mandatory +updateJSONURL="" # blank for now! +displayURL="https://github.com/MinecraftModDevelopmentMods/OreSpawn/" +logoFile="orespawn.png" #optional +authors="Dr. Plantabyte (aka: Cyanobacterium, Dr. Cyano), Jriwanek, DShadowWolf, others" #optional # The description text for the mod (multi line!) (#mandatory) description=''' -This is a long form description of the mod. You can write whatever you want here - -Have some lorem ipsum. - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. +A Mod for generating ore deposits in the world. Does not contain dragon girlfriends, mobs or anything like that ''' # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.examplemod]] #optional - # the modid of the dependency +[[dependencies.orespawn]] #optional modId="forge" #mandatory - # Does this dependency have to exist - if not, ordering below must be specified mandatory=true #mandatory - # The version range of the dependency - versionRange="[25,)" #mandatory - # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory + versionRange="[36,)" #mandatory ordering="NONE" - # Side this dependency is applied on - BOTH, CLIENT or SERVER side="BOTH" -# Here's another dependency -[[dependencies.examplemod]] + +[[dependencies.orespawn]] modId="minecraft" mandatory=true - versionRange="[1.13.2]" + versionRange="[1.16,)" ordering="NONE" side="BOTH" \ No newline at end of file diff --git a/src/main/resources/assets/orespawn4-data/features/features.json b/src/main/resources/assets/orespawn4-data/features/features.json new file mode 100644 index 0000000..09075a3 --- /dev/null +++ b/src/main/resources/assets/orespawn4-data/features/features.json @@ -0,0 +1,26 @@ +[ + { + "name": "default", + "class": "com.mcmoddev.orespawn.impl.features.DefaultFeatureGenerator" + }, + { + "name": "vein", + "class": "com.mcmoddev.orespawn.impl.features.VeinGenerator" + }, + { + "name": "normal-cloud", + "class": "com.mcmoddev.orespawn.impl.features.NormalCloudGenerator" + }, + { + "name": "precision", + "class": "com.mcmoddev.orespawn.impl.features.PrecisionGenerator" + }, + { + "name": "clusters", + "class": "com.mcmoddev.orespawn.impl.features.ClusterGenerator" + }, + { + "name": "underfluids", + "class": "com.mcmoddev.orespawn.impl.features.UnderFluid" + } +] \ No newline at end of file diff --git a/src/main/resources/assets/orespawn4-data/orespawn.json b/src/main/resources/assets/orespawn4-data/orespawn.json new file mode 100644 index 0000000..4021c4f --- /dev/null +++ b/src/main/resources/assets/orespawn4-data/orespawn.json @@ -0,0 +1,332 @@ +{ + "version": "2.0", + "spawns": { + "quartz_ore": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [ + -1 + ], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 15, + "variation": 4, + "frequency": 7, + "minHeight": 0, + "maxHeight": 128 + }, + "blocks": [ + { + "name": "minecraft:quartz_ore", + "chance": 100 + } + ] + }, + "coal_ore": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 25, + "variation": 12, + "frequency": 20, + "minHeight": 0, + "maxHeight": 128 + }, + "blocks": [ + { + "name": "minecraft:coal_ore", + "chance": 100 + } + ] + }, + "iron_ore": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 8, + "variation": 4, + "frequency": 20, + "minHeight": 0, + "maxHeight": 64 + }, + "blocks": [ + { + "name": "minecraft:iron_ore", + "chance": 100 + } + ] + }, + "gold_ore_standard": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 8, + "variation": 2, + "frequency": 2, + "minHeight": 0, + "maxHeight": 32 + }, + "blocks": [ + { + "name": "minecraft:gold_ore", + "chance": 100 + } + ] + }, + "gold_ore_mesa": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "includes": [ "MESA" ] + }, + "parameters": { + "size": 8, + "variation": 2, + "frequency": 2, + "minHeight": 32, + "maxHeight": 79 + }, + "blocks": [ + { + "name": "minecraft:gold_ore", + "chance": 100 + } + ] + }, + "diamond_ore": { + "retrogen": false, + "enabled": true, + "feature": "precision", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 3, + "numObjects": 2, + "minHeight": 0, + "maxHeight": 16 + }, + "blocks": [ + { + "name": "minecraft:diamond_ore", + "chance": 100 + } + ] + }, + "redstone_ore": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 6, + "variation": 3, + "frequency": 8, + "minHeight": 0, + "maxHeight": 16 + }, + "blocks": [ + { + "name": "minecraft:redstone_ore", + "chance": 100 + } + ] + }, + "lapis_ore": { + "retrogen": false, + "enabled": false, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 5, + "variation": 2, + "frequency": 1, + "minHeight": 0, + "maxHeight": 32 + }, + "blocks": [ + { + "name": "minecraft:lapis_ore", + "chance": 100 + } + ] + }, + "emerald_ore": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "includes": [ "MOUNTAIN" ] + }, + "parameters": { + "size": 1, + "variation": 0, + "frequency": 8, + "minHeight": 4, + "maxHeight": 32 + }, + "blocks": [ + { + "name": "minecraft:emerald_ore", + "chance": 100 + } + ] + }, + "dirt": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 112, + "variation": 50, + "frequency": 10, + "minHeight": 0, + "maxHeight": 255 + }, + "blocks": [ + { + "name": "minecraft:dirt", + "chance": 100, + "state": "snowy=false,variant=dirt" + } + ] + }, + "gravel": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 112, + "variation": 50, + "frequency": 8, + "minHeight": 0, + "maxHeight": 255 + }, + "blocks": [ + { + "name": "minecraft:gravel", + "chance": 100 + } + ] + }, + "andesite": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 112, + "variation": 50, + "frequency": 10, + "minHeight": 0, + "maxHeight": 255 + }, + "blocks": [ + { + "name": "minecraft:stone", + "chance": 100, + "state": "variant=andesite" + } + ] + }, + "diorite": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 112, + "variation": 50, + "frequency": 10, + "minHeight": 0, + "maxHeight": 255 + }, + "blocks": [ + { + "name": "minecraft:stone", + "chance": 100, + "state": "variant=diorite" + } + ] + }, + "granite": { + "retrogen": false, + "enabled": true, + "feature": "default", + "replaces": "default", + "dimensions": [], + "biomes": { + "excludes": [] + }, + "parameters": { + "size": 112, + "variation": 50, + "frequency": 10, + "minHeight": 0, + "maxHeight": 255 + }, + "blocks": [ + { + "name": "minecraft:stone", + "chance": 100, + "state": "variant=granite" + } + ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/orespawn4-data/replacements/replacements.json b/src/main/resources/assets/orespawn4-data/replacements/replacements.json new file mode 100644 index 0000000..8e378cf --- /dev/null +++ b/src/main/resources/assets/orespawn4-data/replacements/replacements.json @@ -0,0 +1,8 @@ +{ + "default": [ { "name": "minecraft:stone" }, + { "name": "minecraft:andesite" }, + { "name": "minecraft:diorite" }, + { "name": "minecraft:granite" }, + { "name": "minecraft:netherrack" }, + { "name": "minecraft:end_stone" } ] +} From 81e69d15a914e84bfab0b5165a37822ada749f66 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sun, 4 Jul 2021 19:53:12 -0400 Subject: [PATCH 03/37] Start move from annotation based integration to data based integration 1) Implement resource scan to look for files under "/assets/orespawn-data" in all loaded resources 2) Implement scan of "config/mmd-orespawn-4" for all files 3) Load data files from both sources 4) (not yet done) Remove annotation based system remnants --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 140 ++------ .../com/mcmoddev/orespawn/api/OS4API.java | 324 +++++++++++------- .../orespawn/builders/DimensionMatcher.java | 4 +- .../orespawn/builders/SpawnBuilder.java | 22 +- .../com/mcmoddev/orespawn/data/Constants.java | 10 +- .../mcmoddev/orespawn/data/OS4BlockData.java | 2 +- .../orespawn/loaders/ResourceLoader.java | 187 ++++++++++ 7 files changed, 441 insertions(+), 248 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 7b0921d..06798a7 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,139 +1,49 @@ package com.mcmoddev.orespawn; -import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.mcmoddev.orespawn.data.Constants; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.LinkedList; -import java.util.stream.Collectors; -import java.util.List; -import java.util.Locale; - -import com.google.common.base.Joiner; +import com.mcmoddev.orespawn.loaders.ResourceLoader; @Mod("orespawn") public class OreSpawn { - // Directly reference a log4j logger. - public static final Logger LOGGER = LogManager.getLogger(); - - public OreSpawn() { - // 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 the doClientStuff method for modloading - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); + // Directly reference a log4j logger. + public static final Logger LOGGER = LogManager.getLogger(); - // Register ourselves for server and other game events we are interested in - MinecraftForge.EVENT_BUS.register(this); - } + public OreSpawn() { + // 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 the doClientStuff method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); - private List iterateFiles(ModFile modFile) { - try { - Path root = modFile.getLocator().findPath(modFile, "assets", "orespawn4-data").toAbsolutePath(); - - return Files.walk(root). - map(path -> root.relativize(path.toAbsolutePath())). - filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds - filter(path -> path.toString().endsWith(".json")). - map(path -> Joiner.on('/').join(path)). - map(path -> path.toString()). - map(path -> path.substring(0, path.length() - 5)). - map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)). - collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } - - List iterateDiskFiles() { - try { - Path diskPath = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4).toAbsolutePath(); - return Files.walk(diskPath) - .map(path -> diskPath.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64) - .filter(path -> path.toString().endsWith(".json")) - .map(path -> Joiner.on('/').join(path)) - .map(path -> path.toString()) - .map(path -> path.toLowerCase(Locale.US)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation("orespawn-disk", path)) - .collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } - private void setup(final FMLCommonSetupEvent event) { - List foundFiles = new LinkedList<>(); - ModList.get().getModFiles().stream() - .map(mfi -> iterateFiles(mfi.getFile())) - .forEach(lrl -> lrl.stream().forEach(rl -> foundFiles.add(rl))); + // Register ourselves for server and other game events we are interested in + MinecraftForge.EVENT_BUS.register(this); + } - iterateDiskFiles().stream().forEach(rl -> foundFiles.add(rl)); - - List featuresFiles = foundFiles.stream() - .filter(rl -> rl.getPath().startsWith("features/")) - .collect(Collectors.toList()); - List replacementsFiles = foundFiles.stream() - .filter(rl -> rl.getPath().startsWith("replacements/")) - .collect(Collectors.toList()); - List presetsFiles = foundFiles.stream() - .filter(rl -> rl.getPath().startsWith("presets/")) - .collect(Collectors.toList()); - List spawnConfigs = foundFiles.stream() - .filter(rl -> !featuresFiles.contains(rl)) - .filter(rl -> !replacementsFiles.contains(rl)) - .filter(rl -> !presetsFiles.contains(rl)) - .collect(Collectors.toList()); + private void setup(final FMLCommonSetupEvent event) { + ResourceLoader loader = new ResourceLoader(); + loader.runLoaders(); + } - LOGGER.info("Found {} features files, {} replacements files, {} presets files and {} spawn configuration files", - featuresFiles.size(), replacementsFiles.size(), presetsFiles.size(), spawnConfigs.size()); - - LOGGER.info("> Features Files:"); - featuresFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); - LOGGER.info("> Replacements Files:"); - replacementsFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); - LOGGER.info("> Presets Files:"); - presetsFiles.forEach(rl -> LOGGER.info(">> {}", rl.toString())); - LOGGER.info("> Spawn Configs:"); - spawnConfigs.forEach(rl -> LOGGER.info(">> {}", rl.toString())); - - // load configs - /* - OS4API.loadPresets(); - OS4API.loadReplacements(); - OS4API.loadFeatures(); - OS4API.loadIntegrationWhitelist(); - OS4API.loadSpawns(); - */ - // register world gen - } - private void doClientStuff(final FMLClientSetupEvent event) { - } + private void doClientStuff(final FMLClientSetupEvent event) { + } - private void enqueueIMC(final InterModEnqueueEvent event) { - } + private void enqueueIMC(final InterModEnqueueEvent event) { + } - private void processIMC(final InterModProcessEvent event) { - } + private void processIMC(final InterModProcessEvent event) { + } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index aa4f8fa..ea9cbcb 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -1,6 +1,8 @@ package com.mcmoddev.orespawn.api; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -20,7 +22,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; - +import com.google.gson.stream.JsonReader; import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.builders.SpawnBuilder; import com.mcmoddev.orespawn.data.Constants; @@ -33,11 +35,11 @@ public class OS4API { private static Map modRefs = new HashMap<>(1024); private static List allowedConfigs = new LinkedList<>(); - + public static void addMod(final String modId, final String modPath) { modRefs.put(modId, modPath); } - + public static Map getMods() { return Collections.unmodifiableMap(modRefs); } @@ -48,118 +50,118 @@ public static Map getMods() { public static void loadPresets() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/presets-*.json"); - + // find all `presets_XXX.json` files try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { stream.filter(matcher::matches).map(Path::toFile) - // load them into a registry - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonObject elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonObject(); - - elements.entrySet().stream() - .forEach( entry -> { - String entryZone = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet().stream() - .forEach( subEnt -> { - String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); - PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); - }); - - }); + // load them into a registry + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonObject elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( entry -> { + String entryZone = entry.getKey(); + entry.getValue().getAsJsonObject().entrySet().stream() + .forEach( subEnt -> { + String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); + PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); }); + + }); + }); } catch (final IOException e) { OreSpawn.LOGGER.error(e.getMessage()); } } - + /* * Walk the config directory, load all the various `replacements-XXX.json` files */ public static void loadReplacements() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/replacements-*.json"); - + try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { stream.filter(matcher::matches).map(Path::toFile) - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonObject elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonObject(); - - elements.entrySet().stream() - .forEach( repl -> { - String replName = repl.getKey(); - JsonArray replValues = repl.getValue().getAsJsonArray(); - List blocks = new LinkedList(); - for( JsonElement el : replValues) { - JsonObject zz = el.getAsJsonObject(); - String blName = zz.get("name").getAsString(); - String state = zz.has("state")?zz.get("state").getAsString():""; - blocks.add(new OS4BlockData(blName, state)); - } - ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); - }); - }); + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonObject elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( repl -> { + String replName = repl.getKey(); + JsonArray replValues = repl.getValue().getAsJsonArray(); + List blocks = new LinkedList(); + for( JsonElement el : replValues) { + JsonObject zz = el.getAsJsonObject(); + String blName = zz.get("name").getAsString(); + String state = zz.has("state")?zz.get("state").getAsString():""; + blocks.add(new OS4BlockData(blName, state)); + } + ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); + }); + }); } catch (final IOException e) { OreSpawn.LOGGER.error(e.getMessage()); } } - + /* * Walk the config directory, load all the various `features-XXX.json` files */ public static void loadFeatures() { PathMatcher matcher = FileSystems.getDefault() .getPathMatcher("glob:**/features-*.json"); - + try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { stream.filter(matcher::matches).map(Path::toFile) - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonArray elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonArray(); - - elements.forEach( ent -> { - JsonObject entry = ent.getAsJsonObject(); - String featureName = entry.get("name").getAsString(); - String className = entry.get("class").getAsString(); - FeaturesRegistry.INSTANCE.addFeature(featureName, className); - }); - }); + .forEach( file -> { + final JsonParser parser = new JsonParser(); + String rawJson; + JsonArray elements; + + try { + rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(e.getMessage()); + return; + } + + elements = parser.parse(rawJson).getAsJsonArray(); + + elements.forEach( ent -> { + JsonObject entry = ent.getAsJsonObject(); + String featureName = entry.get("name").getAsString(); + String className = entry.get("class").getAsString(); + FeaturesRegistry.INSTANCE.addFeature(featureName, className); + }); + }); } catch (final IOException e) { OreSpawn.LOGGER.error(e.getMessage()); } } - + /* * Load the `Active Configs` file * This one needs a bit more thought, right now its just a list of filenames... @@ -179,45 +181,117 @@ public static void loadIntegrationWhitelist() { parser.parse(rawJson).getAsJsonArray().forEach( item -> allowedConfigs.add(item.getAsString())); } - - public static void loadSpawns() { - final JsonParser parser = new JsonParser(); - - Arrays.asList(Constants.JSONPATH.toFile().listFiles()) - .stream() - // find all JSON files - .filter( f -> f.toPath().endsWith(".json")) - // that are not in SYSCONF - .filter( f -> !f.getAbsolutePath().contains(Constants.FileBits.SYSCONF)) - // and are listed in allowedConfigs - .filter( f -> allowedConfigs.contains(f.getName().substring(0, f.getName().length() - 5))) - .forEach( inFile -> { - // try to read the file - String rawData; - try { - rawData = FileUtils.readFileToString(inFile, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(String.format("Cannot load %s:\n%s", inFile.getName(), e.getMessage())); - return; - } - - // parse the file - JsonObject rawJson = parser.parse(rawData).getAsJsonObject(); - - String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); - JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); - - OreSpawn.LOGGER.info("Loading JSON version {} from {}", fileVersion, inFile.getName()); - - // load the spawns - spawnWrapper.entrySet().stream() - .forEach( entry -> { - String spawnName = entry.getKey(); - JsonObject spawnData = entry.getValue().getAsJsonObject(); - SpawnBuilder theBuilder = new SpawnBuilder(spawnName); - spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); - SpawnStore.add(spawnName, theBuilder.build()); - }); - }); - } - } + + public static void loadSpawns() { + final JsonParser parser = new JsonParser(); + + Arrays.asList(Constants.JSONPATH.toFile().listFiles()) + .stream() + // find all JSON files + .filter( f -> f.toPath().endsWith(".json")) + // that are not in SYSCONF + .filter( f -> !f.getAbsolutePath().contains(Constants.FileBits.SYSCONF)) + // and are listed in allowedConfigs + .filter( f -> allowedConfigs.contains(f.getName().substring(0, f.getName().length() - 5))) + .forEach( inFile -> { + // try to read the file + String rawData; + try { + rawData = FileUtils.readFileToString(inFile, Charset.defaultCharset()); + } catch (final IOException e) { + OreSpawn.LOGGER.error(String.format("Cannot load %s:\n%s", inFile.getName(), e.getMessage())); + return; + } + + // parse the file + JsonObject rawJson = parser.parse(rawData).getAsJsonObject(); + + String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); + JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); + + OreSpawn.LOGGER.info("Loading JSON version {} from {}", fileVersion, inFile.getName()); + + // load the spawns + spawnWrapper.entrySet().stream() + .forEach( entry -> { + String spawnName = entry.getKey(); + JsonObject spawnData = entry.getValue().getAsJsonObject(); + SpawnBuilder theBuilder = new SpawnBuilder(spawnName); + spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); + SpawnStore.add(spawnName, theBuilder.build()); + }); + }); + } + + private static JsonElement getParsedJson(InputStream stream) { + InputStreamReader readerForStream = new InputStreamReader(stream); + JsonReader jsonReader = new JsonReader(readerForStream); + JsonParser parserToReturnFrom = new JsonParser(); + return parserToReturnFrom.parse(jsonReader); + } + + public static void loadSpawnsFromStream(InputStream inFile) { + JsonObject rawJson = getParsedJson(inFile).getAsJsonObject(); + + String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); + JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); + + OreSpawn.LOGGER.info("Loading JSON version {}", fileVersion); + + // load the spawns + spawnWrapper.entrySet().stream() + .forEach( entry -> { + String spawnName = entry.getKey(); + OreSpawn.LOGGER.info("Loading spawn {} -- {}", spawnName, entry.getValue()); + JsonObject spawnData = entry.getValue().getAsJsonObject(); + SpawnBuilder theBuilder = new SpawnBuilder(spawnName); + spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); + SpawnStore.add(spawnName, theBuilder.build()); + }); + + } + + public static void loadPresetsFromStream(InputStream inFile) { + JsonObject elements = getParsedJson(inFile).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( entry -> { + String entryZone = entry.getKey(); + entry.getValue().getAsJsonObject().entrySet().stream() + .forEach( subEnt -> { + String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); + PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); + }); + + }); + } + + public static void loadReplacementsFromStream(InputStream inFile) { + JsonObject elements = getParsedJson(inFile).getAsJsonObject(); + + elements.entrySet().stream() + .forEach( repl -> { + String replName = repl.getKey(); + JsonArray replValues = repl.getValue().getAsJsonArray(); + List blocks = new LinkedList(); + for( JsonElement el : replValues) { + JsonObject zz = el.getAsJsonObject(); + String blName = zz.get("name").getAsString(); + String state = zz.has("state")?zz.get("state").getAsString():""; + blocks.add(new OS4BlockData(blName, state)); + } + ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); + }); + } + + public static void loadFeaturesFromStream(InputStream inFile) { + JsonArray elements = getParsedJson(inFile).getAsJsonArray(); + + elements.forEach( ent -> { + JsonObject entry = ent.getAsJsonObject(); + String featureName = entry.get("name").getAsString(); + String className = entry.get("class").getAsString(); + FeaturesRegistry.INSTANCE.addFeature(featureName, className); + }); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java index 31f713f..a185b6e 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java @@ -1,10 +1,10 @@ package com.mcmoddev.orespawn.builders; -import com.google.gson.JsonObject; +import com.google.gson.JsonArray; public class DimensionMatcher { - public DimensionMatcher(JsonObject asJsonObject) { + public DimensionMatcher(JsonArray jsonArray) { // TODO Auto-generated constructor stub } diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java index faa0515..d8d494b 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java @@ -5,6 +5,7 @@ import java.util.List; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mcmoddev.orespawn.OreSpawn; @@ -42,17 +43,26 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { this.setFeature(actualData); break; case Constants.ConfigNames.DIMENSIONS: - this.setDimensions(new DimensionMatcher(actualData.getAsJsonObject())); + this.setDimensions(new DimensionMatcher(actualData.getAsJsonArray())); break; case Constants.ConfigNames.BIOMES: this.setBiomes(new BiomeMatcher(actualData.getAsJsonObject())); break; case Constants.ConfigNames.BLOCKS: - this.setBlocks(OS4BlockData.parseJsonData(actualData)); + JsonArray blocks = actualData.getAsJsonArray(); + blocks.forEach(block -> { + this.addBlock(OS4BlockData.parseJsonData(block)); + }); break; case Constants.ConfigNames.PARAMETERS: this.parameters = actualData.getAsJsonObject(); break; + case Constants.ConfigNames.RETROGEN: + this.doRetro = actualData.getAsBoolean(); + break; + case Constants.ConfigNames.ENABLED: + this.active = actualData.getAsBoolean(); + break; default: OreSpawn.LOGGER.error("Unknown config item {} - skipping", itemName); } @@ -60,11 +70,17 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { return this; } - private void setBlocks(Object parseJsonData) { + private void addBlock(OS4BlockData block) { // TODO Auto-generated method stub } + /* + * private void setBlocks(Object parseJsonData) { // TODO Auto-generated method + * stub + * + * } + */ private void setBiomes(BiomeMatcher biomeMatcher) { // TODO Auto-generated method stub diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index 5b9e1a6..fb4dfaa 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -10,7 +10,7 @@ public class Constants { public static final Path JSONPATH = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4); public static class ConfigNames { - public static final String REPLACEMENT = "replacements"; + public static final String REPLACEMENT = "replaces"; public static final String FEATURE = "feature"; public static final String DIMENSIONS = "dimensions"; public static final String BIOMES = "biomes"; @@ -18,7 +18,8 @@ public static class ConfigNames { public static final String PARAMETERS = "parameters"; public static final String VERSION = "version"; public static final String SPAWNS = "spawns"; - + public static final String ENABLED = "enabled"; + public static final String RETROGEN = "retrogen"; } public static class FileBits { public static final String CONFIG_DIR = "config"; @@ -26,5 +27,10 @@ public static class FileBits { public static final String SYSCONF = "sysconf"; public static final String PRESETS = "presets.json"; public static final String ALLOWED_MODS = "active_mods.json"; + public static final String DISK = "__DISK__"; + public static final String RESOURCE = "__RESOURCE__"; + } + public static enum FileTypes { + FEATURES, SPAWN, PRESETS, REPLACEMENTS; } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java index 679904a..c1100a6 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java @@ -24,7 +24,7 @@ public final String getBlockState() { return this.blockState; } - public static Object parseJsonData(JsonElement itemData) { + public static OS4BlockData parseJsonData(JsonElement itemData) { // TODO Auto-generated method stub return null; } diff --git a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java new file mode 100644 index 0000000..ead0077 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java @@ -0,0 +1,187 @@ +package com.mcmoddev.orespawn.loaders; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import com.google.common.base.Joiner; +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS4API; +import com.mcmoddev.orespawn.data.Constants; +import com.mcmoddev.orespawn.data.Constants.FileTypes; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; + +public class ResourceLoader { + public void runLoaders() { + List foundFiles = new LinkedList<>(); + ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) + .forEach(lrl -> lrl.stream().forEach(rl -> foundFiles.add(rl))); + + iterateDiskFiles().stream().forEach(rl -> foundFiles.add(rl)); + + List featuresFiles = foundFiles.stream().filter(rl -> rl.getPath().startsWith("features/")) + .collect(Collectors.toList()); + List replacementsFiles = foundFiles.stream() + .filter(rl -> rl.getPath().startsWith("replacements/")).collect(Collectors.toList()); + List presetsFiles = foundFiles.stream().filter(rl -> rl.getPath().startsWith("presets/")) + .collect(Collectors.toList()); + List spawnConfigs = foundFiles.stream().filter(rl -> !featuresFiles.contains(rl)) + .filter(rl -> !replacementsFiles.contains(rl)).filter(rl -> !presetsFiles.contains(rl)) + .collect(Collectors.toList()); + + OreSpawn.LOGGER.info("Found {} features files, {} replacements files, {} presets files and {} spawn configuration files", + featuresFiles.size(), replacementsFiles.size(), presetsFiles.size(), spawnConfigs.size()); + + OreSpawn.LOGGER.info("> Features Files:"); + featuresFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); + OreSpawn.LOGGER.info("> Replacements Files:"); + replacementsFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); + OreSpawn.LOGGER.info("> Presets Files:"); + presetsFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); + OreSpawn.LOGGER.info("> Spawn Configs:"); + spawnConfigs.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); + + + if (Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, Constants.FileBits.ALLOWED_MODS).toFile().exists()) { + OS4API.loadIntegrationWhitelist(); + } + + // TODO: Add Whitelist/Blacklist filtering + featuresFiles.stream().forEach(rl -> { + if (rl.getNamespace().matches("orespawn-disk")) { + loadFromDisk(rl, Constants.FileTypes.FEATURES); + } else { + loadFromResource(rl, Constants.FileTypes.FEATURES); + } + }); + + replacementsFiles.stream().forEach(rl -> { + if (rl.getNamespace().matches("orespawn-disk")) { + loadFromDisk(rl, Constants.FileTypes.REPLACEMENTS); + } else { + loadFromResource(rl, Constants.FileTypes.REPLACEMENTS); + } + }); + + presetsFiles.stream().forEach(rl -> { + if (rl.getNamespace().matches("orespawn-disk")) { + loadFromDisk(rl, Constants.FileTypes.PRESETS); + } else { + loadFromResource(rl, Constants.FileTypes.PRESETS); + } + }); + + spawnConfigs.stream().forEach(rl -> { + if (rl.getNamespace().matches("orespawn-disk")) { + loadFromDisk(rl, Constants.FileTypes.SPAWN); + } else { + loadFromResource(rl, Constants.FileTypes.SPAWN); + } + }); + } + + private List iterateFiles(ModFile modFile) { + try { + Path root = modFile.getLocator().findPath(modFile, "assets", "orespawn4-data").toAbsolutePath(); + + return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds + filter(path -> path.toString().endsWith(".json")).map(path -> Joiner.on('/').join(path)) + .map(path -> path.toString()).map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) + .collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } + + List iterateDiskFiles() { + try { + Path diskPath = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4).toAbsolutePath(); + return Files.walk(diskPath).map(path -> diskPath.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64).filter(path -> path.toString().endsWith(".json")) + .map(path -> Joiner.on('/').join(path)).map(path -> path.toString()) + .map(path -> path.toLowerCase(Locale.US)).map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation("orespawn-disk", path)).collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } + + private void loadFromDisk(ResourceLocation rl, FileTypes type) { + Path fp = makePath(rl, Constants.FileBits.DISK); + try { + switch(type) { + case FEATURES: + OS4API.loadFeaturesFromStream(Files.newInputStream(fp)); + break; + case PRESETS: + OS4API.loadPresetsFromStream(Files.newInputStream(fp)); + break; + case REPLACEMENTS: + OS4API.loadReplacementsFromStream(Files.newInputStream(fp)); + break; + case SPAWN: + OS4API.loadSpawnsFromStream(Files.newInputStream(fp)); + break; + default: + OreSpawn.LOGGER.error("Asked to load {} of type {} - I don't know this type of data", rl.toString(), type); + break; + + } + } catch (IOException e) { + OreSpawn.LOGGER.error("Exception loading resource {} -- {}", rl.toString(), e.getMessage()); + e.printStackTrace(); + } + } + + private void loadFromResource(ResourceLocation rl, FileTypes type) { + Path fp = makePath(rl, Constants.FileBits.RESOURCE); + try { + switch(type) { + case FEATURES: + OS4API.loadFeaturesFromStream(Files.newInputStream(fp)); + break; + case PRESETS: + OS4API.loadPresetsFromStream(Files.newInputStream(fp)); + break; + case REPLACEMENTS: + OS4API.loadReplacementsFromStream(Files.newInputStream(fp)); + break; + case SPAWN: + OS4API.loadSpawnsFromStream(Files.newInputStream(fp)); + break; + default: + OreSpawn.LOGGER.error("Asked to load {} of type {} - I don't know this type of data", rl.toString(), type); + break; + + } + } catch (IOException e) { + OreSpawn.LOGGER.error("Exception loading resource {} -- {}", rl.toString(), e.getMessage()); + e.printStackTrace(); + } + } + + private Path makePath(ResourceLocation rl, String type) { + switch(type) { + case Constants.FileBits.DISK: + return Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, rl.getPath()+".json"); + case Constants.FileBits.RESOURCE: + ModFile mf = ModList.get().getModFileById(rl.getNamespace()).getFile(); + return mf.getLocator().findPath(mf, "assets", "orespawn4-data", rl.getPath()+".json"); + default: + OreSpawn.LOGGER.error("Asked to resolve a path for {} of type {} -- I do not know how to do this", rl.toString(), type); + return null; + } + } + +} From abec52b73bebd741755328ce04974fa2a2489398 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sun, 4 Jul 2021 20:17:55 -0400 Subject: [PATCH 04/37] Abstract a constant and remove now unused code 1) Abstract out the "assets/orespawn-data" path-string as a constant 2) Remove the remnants of the old annotation based integration system --- .../orespawn/api/plugin/PluginLoader.java | 108 ------------------ .../com/mcmoddev/orespawn/data/Constants.java | 1 + 2 files changed, 1 insertion(+), 108 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java diff --git a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java b/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java deleted file mode 100644 index 174e904..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/plugin/PluginLoader.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.mcmoddev.orespawn.api.plugin; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Iterator; -import java.util.Locale; -import java.util.stream.Stream; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4API; -import com.mcmoddev.orespawn.api.OS4Plugin; -import com.mcmoddev.orespawn.data.Config; -import com.mcmoddev.orespawn.data.Constants; - -public enum PluginLoader { - INSTANCE; - - public static void Load(Class clazz) { - if( clazz.isAnnotationPresent(OS4Plugin.class) ) { - loadInternal(clazz); - } - } - - private static void loadInternal(Class cls) { - final OS4Plugin annot = cls.getAnnotation(OS4Plugin.class); - OS4API.addMod(annot.modid(), annot.resourcePath()); - } - - public static void findResources() { - OS4API.getMods().entrySet() - .forEach( ent -> { - final String base = String.format(Locale.ENGLISH, "assets/%s/%s", ent.getKey(), ent.getValue()); - final URL resURL = INSTANCE.getClass().getClassLoader().getResource(base); - - URI uri; - - try { - uri = resURL.toURI(); - } catch (URISyntaxException ex) { - OreSpawn.LOGGER.error(ex.getMessage()); - return; - } - - if (uri.getScheme().equals("jar")) { - try (FileSystem fileSystem = FileSystems.newFileSystem(uri, - Collections.emptyMap())) { - copyout(fileSystem.getPath(base), ent.getKey()); - } catch (IOException exc) { - OreSpawn.LOGGER.error(exc.getMessage()); - return; - } - } else { - copyout(Paths.get(uri), ent.getKey()); } - - Config.addKnownMod(ent.getKey()); - - }); - } - - private static void copyout(final Path myPath, final String modId) { - try (Stream walk = Files.walk(myPath, 1)) { - for (final Iterator it = walk.iterator(); it.hasNext();) { - final Path p = it.next(); - final String name = p.getFileName().toString(); - - if ("json".equals(FilenameUtils.getExtension(name))) { - InputStream reader = null; - Path target; - - if ("_features".equals(FilenameUtils.getBaseName(name))) { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, - Constants.FileBits.SYSCONF, - String.format(Locale.ENGLISH, "features-%s.json", modId)); - } else if ("_replacements".equals(FilenameUtils.getBaseName(name))) { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, - Constants.FileBits.SYSCONF, - String.format(Locale.ENGLISH, "replacements-%s.json", modId)); - } else { - target = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, - String.format(Locale.ENGLISH, "%s.json", modId)); - } - - if (!target.toFile().exists()) { - reader = Files.newInputStream(p); - FileUtils.copyInputStreamToFile(reader, target.toFile()); - IOUtils.closeQuietly(reader); - } - } - } - } catch (IOException exc) { - OreSpawn.LOGGER.error(exc.getMessage()); - } - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index fb4dfaa..c33514d 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -29,6 +29,7 @@ public static class FileBits { public static final String ALLOWED_MODS = "active_mods.json"; public static final String DISK = "__DISK__"; public static final String RESOURCE = "__RESOURCE__"; + public static final String RESOURCE_PATH = "/assets/orespawn-data"; } public static enum FileTypes { FEATURES, SPAWN, PRESETS, REPLACEMENTS; From ada1e46e1d2bd3d955c6f9921763f2dd7f513251 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sun, 4 Jul 2021 21:02:48 -0400 Subject: [PATCH 05/37] Add a proper blocklist/blacklist for controlling what mods configs get loaded and what configs proper get loaded --- .../orespawn/data/ConfigBlacklist.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java diff --git a/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java b/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java new file mode 100644 index 0000000..36cfa43 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java @@ -0,0 +1,29 @@ +package com.mcmoddev.orespawn.data; + +import java.util.List; +import java.util.LinkedList; + +public class ConfigBlacklist { + private static final List blockedMods = new LinkedList<>(); + private static final List blockedConfigs = new LinkedList<>(); + + public ConfigBlacklist() { + + } + + public void addMod(final String modid) { + blockedMods.add(modid); + } + + public void addConfig(final String configName) { + blockedConfigs.add(configName); + } + + public boolean isAllowed(final String checkName) { + return !(blockedMods.contains(checkName) || blockedConfigs.contains(checkName)); + } + + public boolean isBlocked(final String checkName) { + return !isAllowed(checkName); + } +} From 53fcae79bdd67ca782b6bc528593f8a1618c43c1 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sun, 4 Jul 2021 21:03:39 -0400 Subject: [PATCH 06/37] Several changes: 1) Implement use of Blocklist/Blacklist created in previous commit 2) Refactoring to split out shared code and make json config loading more streamlined --- .../com/mcmoddev/orespawn/api/OS4API.java | 177 ++---------------- .../orespawn/builders/SpawnBuilder.java | 7 - .../orespawn/loaders/ResourceLoader.java | 51 +++-- 3 files changed, 32 insertions(+), 203 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index ea9cbcb..e9153ba 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -4,17 +4,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; -import java.nio.file.FileSystems; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.stream.Stream; import org.apache.commons.io.FileUtils; @@ -25,6 +20,7 @@ import com.google.gson.stream.JsonReader; import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.builders.SpawnBuilder; +import com.mcmoddev.orespawn.data.ConfigBlacklist; import com.mcmoddev.orespawn.data.Constants; import com.mcmoddev.orespawn.data.OS4BlockData; import com.mcmoddev.orespawn.registries.FeaturesRegistry; @@ -34,7 +30,7 @@ public class OS4API { private static Map modRefs = new HashMap<>(1024); - private static List allowedConfigs = new LinkedList<>(); + private static ConfigBlacklist configBlacklist = new ConfigBlacklist(); public static void addMod(final String modId, final String modPath) { modRefs.put(modId, modPath); @@ -44,124 +40,6 @@ public static Map getMods() { return Collections.unmodifiableMap(modRefs); } - /* - * Walk the config directory, load all the various `presets-XXX.json` files - */ - public static void loadPresets() { - PathMatcher matcher = FileSystems.getDefault() - .getPathMatcher("glob:**/presets-*.json"); - - // find all `presets_XXX.json` files - try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { - stream.filter(matcher::matches).map(Path::toFile) - // load them into a registry - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonObject elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonObject(); - - elements.entrySet().stream() - .forEach( entry -> { - String entryZone = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet().stream() - .forEach( subEnt -> { - String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); - PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); - }); - - }); - }); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - } - } - - /* - * Walk the config directory, load all the various `replacements-XXX.json` files - */ - public static void loadReplacements() { - PathMatcher matcher = FileSystems.getDefault() - .getPathMatcher("glob:**/replacements-*.json"); - - try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { - stream.filter(matcher::matches).map(Path::toFile) - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonObject elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonObject(); - - elements.entrySet().stream() - .forEach( repl -> { - String replName = repl.getKey(); - JsonArray replValues = repl.getValue().getAsJsonArray(); - List blocks = new LinkedList(); - for( JsonElement el : replValues) { - JsonObject zz = el.getAsJsonObject(); - String blName = zz.get("name").getAsString(); - String state = zz.has("state")?zz.get("state").getAsString():""; - blocks.add(new OS4BlockData(blName, state)); - } - ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); - }); - }); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - } - } - - /* - * Walk the config directory, load all the various `features-XXX.json` files - */ - public static void loadFeatures() { - PathMatcher matcher = FileSystems.getDefault() - .getPathMatcher("glob:**/features-*.json"); - - try (final Stream stream = Files.walk(Constants.SYSCONF, 1)) { - stream.filter(matcher::matches).map(Path::toFile) - .forEach( file -> { - final JsonParser parser = new JsonParser(); - String rawJson; - JsonArray elements; - - try { - rawJson = FileUtils.readFileToString(file, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - elements = parser.parse(rawJson).getAsJsonArray(); - - elements.forEach( ent -> { - JsonObject entry = ent.getAsJsonObject(); - String featureName = entry.get("name").getAsString(); - String className = entry.get("class").getAsString(); - FeaturesRegistry.INSTANCE.addFeature(featureName, className); - }); - }); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - } - } - /* * Load the `Active Configs` file * This one needs a bit more thought, right now its just a list of filenames... @@ -179,48 +57,11 @@ public static void loadIntegrationWhitelist() { return; } - parser.parse(rawJson).getAsJsonArray().forEach( item -> allowedConfigs.add(item.getAsString())); - } - - public static void loadSpawns() { - final JsonParser parser = new JsonParser(); - - Arrays.asList(Constants.JSONPATH.toFile().listFiles()) - .stream() - // find all JSON files - .filter( f -> f.toPath().endsWith(".json")) - // that are not in SYSCONF - .filter( f -> !f.getAbsolutePath().contains(Constants.FileBits.SYSCONF)) - // and are listed in allowedConfigs - .filter( f -> allowedConfigs.contains(f.getName().substring(0, f.getName().length() - 5))) - .forEach( inFile -> { - // try to read the file - String rawData; - try { - rawData = FileUtils.readFileToString(inFile, Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(String.format("Cannot load %s:\n%s", inFile.getName(), e.getMessage())); - return; - } - - // parse the file - JsonObject rawJson = parser.parse(rawData).getAsJsonObject(); - - String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); - JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); - - OreSpawn.LOGGER.info("Loading JSON version {} from {}", fileVersion, inFile.getName()); - - // load the spawns - spawnWrapper.entrySet().stream() - .forEach( entry -> { - String spawnName = entry.getKey(); - JsonObject spawnData = entry.getValue().getAsJsonObject(); - SpawnBuilder theBuilder = new SpawnBuilder(spawnName); - spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); - SpawnStore.add(spawnName, theBuilder.build()); - }); - }); + JsonObject topLevel = parser.parse(rawJson).getAsJsonObject(); + if (topLevel.has("mods")) + topLevel.get("mods").getAsJsonArray().forEach(modid -> configBlacklist.addMod(modid.getAsString())); + if (topLevel.has("configs")) + topLevel.get("configs").getAsJsonArray().forEach(configFile -> configBlacklist.addConfig(configFile.getAsString())); } private static JsonElement getParsedJson(InputStream stream) { @@ -294,4 +135,8 @@ public static void loadFeaturesFromStream(InputStream inFile) { FeaturesRegistry.INSTANCE.addFeature(featureName, className); }); } + + public static boolean isAllowed(final String checkName) { + return configBlacklist.isAllowed(checkName); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java index d8d494b..0f120a2 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java @@ -72,15 +72,8 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { private void addBlock(OS4BlockData block) { // TODO Auto-generated method stub - } - /* - * private void setBlocks(Object parseJsonData) { // TODO Auto-generated method - * stub - * - * } - */ private void setBiomes(BiomeMatcher biomeMatcher) { // TODO Auto-generated method stub diff --git a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java index ead0077..cd80993 100644 --- a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java +++ b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java @@ -55,43 +55,34 @@ public void runLoaders() { OS4API.loadIntegrationWhitelist(); } - // TODO: Add Whitelist/Blacklist filtering - featuresFiles.stream().forEach(rl -> { - if (rl.getNamespace().matches("orespawn-disk")) { - loadFromDisk(rl, Constants.FileTypes.FEATURES); - } else { - loadFromResource(rl, Constants.FileTypes.FEATURES); - } - }); + featuresFiles.stream() + .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) + .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.FEATURES)); - replacementsFiles.stream().forEach(rl -> { - if (rl.getNamespace().matches("orespawn-disk")) { - loadFromDisk(rl, Constants.FileTypes.REPLACEMENTS); - } else { - loadFromResource(rl, Constants.FileTypes.REPLACEMENTS); - } - }); + replacementsFiles.stream() + .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) + .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.REPLACEMENTS)); - presetsFiles.stream().forEach(rl -> { - if (rl.getNamespace().matches("orespawn-disk")) { - loadFromDisk(rl, Constants.FileTypes.PRESETS); - } else { - loadFromResource(rl, Constants.FileTypes.PRESETS); - } - }); + presetsFiles.stream() + .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) + .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.PRESETS)); - spawnConfigs.stream().forEach(rl -> { - if (rl.getNamespace().matches("orespawn-disk")) { - loadFromDisk(rl, Constants.FileTypes.SPAWN); - } else { - loadFromResource(rl, Constants.FileTypes.SPAWN); - } - }); + spawnConfigs.stream() + .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) + .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.SPAWN)); + } + + private void loadResourceLocation(final ResourceLocation rl, final FileTypes type) { + if (rl.getNamespace().matches("orespawn-disk")) { + loadFromDisk(rl, type); + } else { + loadFromResource(rl, type); + } } private List iterateFiles(ModFile modFile) { try { - Path root = modFile.getLocator().findPath(modFile, "assets", "orespawn4-data").toAbsolutePath(); + Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds From fb1e191f411863f87f0f1ab94b7422ebe5c1c927 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Sun, 4 Jul 2021 21:52:30 -0400 Subject: [PATCH 07/37] flesh out some stubs, add some needed functionality --- .../orespawn/builders/SpawnBuilder.java | 28 +++++++++++-------- .../mcmoddev/orespawn/data/OS4BlockData.java | 22 +++++++++++++-- .../registries/ReplacementsRegistry.java | 6 ++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java index 0f120a2..fb9d0dc 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java @@ -1,7 +1,9 @@ package com.mcmoddev.orespawn.builders; +import com.mcmoddev.orespawn.registries.FeaturesRegistry; import com.mcmoddev.orespawn.registries.IReplacementEntry; import com.mcmoddev.orespawn.registries.PresetsRegistry; +import com.mcmoddev.orespawn.registries.ReplacementsRegistry; import java.util.List; @@ -9,6 +11,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS4Feature; import com.mcmoddev.orespawn.data.OS4BlockData; import com.mcmoddev.orespawn.data.Constants; @@ -21,6 +24,7 @@ public class SpawnBuilder { private BiomeMatcher biomes; private List blocks; private JsonObject parameters; + private OS4Feature feature; public SpawnBuilder(final String spawnName) { myName = spawnName; @@ -37,10 +41,10 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { switch(itemName) { case Constants.ConfigNames.REPLACEMENT: - this.setReplacement(actualData); + this.setReplacement(actualData.getAsString()); break; case Constants.ConfigNames.FEATURE: - this.setFeature(actualData); + this.setFeature(actualData.getAsString()); break; case Constants.ConfigNames.DIMENSIONS: this.setDimensions(new DimensionMatcher(actualData.getAsJsonArray())); @@ -71,25 +75,23 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { } private void addBlock(OS4BlockData block) { - // TODO Auto-generated method stub + blocks.add(block); } private void setBiomes(BiomeMatcher biomeMatcher) { - // TODO Auto-generated method stub - + biomes = biomeMatcher; } private void setDimensions(DimensionMatcher dimensionMatcher) { - // TODO Auto-generated method stub - + dimensions = dimensionMatcher; } - private void setReplacement(final JsonElement data) { - + private void setReplacement(final String replacementName) { + replacements = ReplacementsRegistry.INSTANCE.get(replacementName); } - private void setFeature(final JsonElement data) { - + private void setFeature(final String featureName) { + feature = FeaturesRegistry.INSTANCE.getFeature(featureName); } public JsonObject getParameters() { @@ -100,6 +102,10 @@ public List getBlocks() { return blocks; } + public OS4Feature getFeature() { + return feature; + } + public String getMyName() { return myName; } diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java index c1100a6..b5e2ff9 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java @@ -1,14 +1,25 @@ package com.mcmoddev.orespawn.data; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; public class OS4BlockData { private final String blockIdentifier; private final String blockState; + private final int chance; - public OS4BlockData(final String blockName, final String state) { + public OS4BlockData(final String blockName, final String state, final int chance) { this.blockIdentifier = blockName; this.blockState = state==null?"":state; + this.chance = chance; + } + + public OS4BlockData(final String blockName, final String state) { + this(blockName, state, 100); + } + + public final int getChance() { + return this.chance; } public final String getBlock() { @@ -25,7 +36,12 @@ public final String getBlockState() { } public static OS4BlockData parseJsonData(JsonElement itemData) { - // TODO Auto-generated method stub - return null; + JsonObject actual = itemData.getAsJsonObject(); + + int chance = actual.has("chance")?actual.get("chance").getAsInt():100; + String state = actual.has("state")?actual.get("state").getAsString():null; + String name = actual.has("name")?actual.get("name").getAsString():"i-am:a-missing-value"; + + return new OS4BlockData(name, state, chance); } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java index 2025a8f..3a6e4d2 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java @@ -41,6 +41,12 @@ public void addReplacement(final IReplacementEntry entry) { replacementsRegistry.register(entry); } + public IReplacementEntry get(final String name) { + ResourceLocation loc = name.contains(":")?new ResourceLocation(name):new ResourceLocation("orespawn", name); + if (replacementsRegistry.containsKey(loc)) return replacementsRegistry.getValue(loc); + else return replacementsRegistry.getValue(new ResourceLocation("orespawn", "default")); + } + private class DefaultReplacementEntry implements IReplacementEntry { private ResourceLocation name; private final List data; From d35aa1b208792820ae76dd4c98fd74d9f5edb63f Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Mon, 5 Jul 2021 16:59:32 -0400 Subject: [PATCH 08/37] Some work on fleshing out biome and dimension allow/deny lists Final resolution of spawns - which need occur, necessarily, at the end of the block registry event (the start of the item registry event, at a minimum) is the next step. Will need to create a BlockMatcher setup to take over for the Replacements and a generic utility for turning OS4BlockData into a BlockState. --- .../orespawn/builders/BiomeMatcher.java | 57 ++++++++++++++++++- .../orespawn/builders/DimensionMatcher.java | 38 ++++++++++++- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java index c9e5c4d..db5e754 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java @@ -1,11 +1,64 @@ package com.mcmoddev.orespawn.builders; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import com.google.gson.JsonArray; import com.google.gson.JsonObject; public class BiomeMatcher { + private final List> allowedBiomes = new LinkedList<>(); + private final List> blockedBiomes = new LinkedList<>(); + private final List rawBiomeWhitelist = new LinkedList<>(); + private final List rawBiomeBlacklist = new LinkedList<>(); + private boolean resolved = false; + + public BiomeMatcher(JsonObject rawData) { + if (rawData.has("whitelist")) + loadList(rawData.get("whitelist").getAsJsonArray(), rawBiomeWhitelist); + if (rawData.has("blacklist")) + loadList(rawData.get("blacklist").getAsJsonArray(), rawBiomeBlacklist); + } + + private void loadList(JsonArray inputData, List theList) { + inputData.forEach(elem -> theList.add(elem.getAsString())); + } + + public void resolve() { + if (!rawBiomeWhitelist.isEmpty()) + allowedBiomes.addAll( rawBiomeWhitelist.stream() + .map( name -> getRegistryKeyFor(name) ) + .collect(Collectors.toList())); + if (!rawBiomeBlacklist.isEmpty()) + blockedBiomes.addAll( rawBiomeBlacklist.stream() + .map( name -> getRegistryKeyFor(name) ) + .collect(Collectors.toList())); + + resolved = true; + } - public BiomeMatcher(JsonObject asJsonObject) { - // TODO Auto-generated constructor stub + private RegistryKey getRegistryKeyFor(String name) { + return RegistryKey.getOrCreateKey(Registry.BIOME_KEY, getResourceLocationFor(name)); } + private ResourceLocation getResourceLocationFor(String name) { + String mod = name.contains(":")?name.split(":")[0]:"minecraft"; + String lname = name.contains(":")?name.split(":")[1]:name; + return new ResourceLocation(mod, lname); + } + + public boolean isAllowed(final String name) { + if (!resolved) return true; + if (allowedBiomes.isEmpty() && blockedBiomes.isEmpty()) return true; + + + RegistryKey key = getRegistryKeyFor(name); + return (allowedBiomes.contains(key) && !blockedBiomes.contains(key)); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java index a185b6e..19166db 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java @@ -1,11 +1,43 @@ package com.mcmoddev.orespawn.builders; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + import com.google.gson.JsonArray; -public class DimensionMatcher { +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.Dimension; - public DimensionMatcher(JsonArray jsonArray) { - // TODO Auto-generated constructor stub +public class DimensionMatcher { + private final List> allowedDimensions; + private final List rawDimensions; + private boolean loaded = false; + + public DimensionMatcher(JsonArray dimensionData) { + allowedDimensions = new LinkedList<>(); + rawDimensions = new LinkedList<>(); + dimensionData.forEach(dim -> rawDimensions.add(dim.getAsString())); } + private RegistryKey getKeyFromString(final String dimensionName) { + String mod = dimensionName.contains(":")?dimensionName.split(":")[0]:"minecraft"; + String name = dimensionName.contains(":")?dimensionName.split(":")[1]:dimensionName; + ResourceLocation loc = new ResourceLocation(mod, name); + return RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, loc); + } + public boolean isDimensionAllowed(final String dimension) { + if (allowedDimensions.isEmpty() || !loaded) return true; + return allowedDimensions.contains(getKeyFromString(dimension)); + } + + public void resolveData() { + allowedDimensions.addAll( + rawDimensions.stream() + .map( rawDim -> getKeyFromString(rawDim) ) + .collect(Collectors.toList())); + loaded = true; + } } From 0b2797d6d9f958917f8d8f20d2816d1b18fc4549 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Tue, 6 Jul 2021 20:02:16 -0400 Subject: [PATCH 09/37] more completed work 1) Resolve data for blocks, dimensions and such in FMLServerStartingEvent 2) Prep work for porting all features 3) Start to finalize setup for world-gen bits --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 8 ++++++ .../com/mcmoddev/orespawn/api/OS4API.java | 8 ++++++ .../orespawn/registries/FeaturesRegistry.java | 21 +++++++++----- .../orespawn/registries/IFeatureEntry.java | 1 + .../registries/IReplacementEntry.java | 6 ++-- .../registries/ReplacementsRegistry.java | 28 +++++++++++++++---- .../orespawn/registries/SpawnStore.java | 7 +++++ .../com/mcmoddev/orespawn/utils/Helpers.java | 24 ++++++++++++++++ .../orespawn/utils/OS4BlockStateMatcher.java | 23 +++++++++++++++ 9 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 06798a7..b5b83a6 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -6,11 +6,13 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.mcmoddev.orespawn.api.OS4API; import com.mcmoddev.orespawn.loaders.ResourceLoader; @Mod("orespawn") @@ -27,6 +29,8 @@ public OreSpawn() { FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC); // Register the doClientStuff method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); + // Register the doClientStuff method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doServerStartTasks); // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.register(this); @@ -46,4 +50,8 @@ private void enqueueIMC(final InterModEnqueueEvent event) { private void processIMC(final InterModProcessEvent event) { } + + private void doServerStartTasks(final FMLServerStartingEvent ev) { + OS4API.resolveData(ev.getServer().getDynamicRegistries()); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index e9153ba..44ca752 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -28,6 +28,8 @@ import com.mcmoddev.orespawn.registries.ReplacementsRegistry; import com.mcmoddev.orespawn.registries.SpawnStore; +import net.minecraft.util.registry.DynamicRegistries; + public class OS4API { private static Map modRefs = new HashMap<>(1024); private static ConfigBlacklist configBlacklist = new ConfigBlacklist(); @@ -139,4 +141,10 @@ public static void loadFeaturesFromStream(InputStream inFile) { public static boolean isAllowed(final String checkName) { return configBlacklist.isAllowed(checkName); } + + public static void resolveData(DynamicRegistries dynamicRegistries) { + FeaturesRegistry.INSTANCE.doDataResolution(); + ReplacementsRegistry.INSTANCE.doDataResolution(dynamicRegistries); + SpawnStore.doResolveData(dynamicRegistries); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java index b31c858..b421d2c 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java @@ -54,16 +54,9 @@ private class DefaultFeatureEntry implements IFeatureEntry { private String featureClassPath; private Class featureClass; - @SuppressWarnings("unchecked") public DefaultFeatureEntry(final ResourceLocation location, final String classPath) { this.resLoc = location; this.featureClassPath = classPath; - try { - this.featureClass = (Class)Class.forName(classPath); - } catch(ClassNotFoundException e) { - OreSpawn.LOGGER.error(e.getMessage()); - this.featureClass = null; - } } public Class getFeature() { @@ -97,5 +90,19 @@ public IFeatureEntry setRegistryName(ResourceLocation arg0) { return this; } + @Override + @SuppressWarnings("unchecked") + public void resolve() { + try { + this.featureClass = (Class)Class.forName(this.featureClassPath); + } catch(ClassNotFoundException e) { + OreSpawn.LOGGER.error(e.getMessage()); + this.featureClass = null; + } + } + } + + public void doDataResolution() { + featuresRegistry.getValues().stream().forEach(ent -> ent.resolve()); } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java index a144ab5..06add54 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java @@ -8,4 +8,5 @@ public interface IFeatureEntry extends IForgeRegistryEntry { Class getFeature(); String getClassName(); void setFeatureName(String name); + void resolve(); } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java index 59bd174..e77d545 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java @@ -3,15 +3,17 @@ import java.util.List; import com.mcmoddev.orespawn.data.OS4BlockData; +import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; import net.minecraft.block.Block; -import net.minecraftforge.common.extensions.IForgeBlockState; +import net.minecraft.block.BlockState; import net.minecraftforge.registries.IForgeRegistryEntry; public interface IReplacementEntry extends IForgeRegistryEntry { List getReplacementData(); - List getReplacementState(); + List getReplacementState(); List getReplacementBlock(); void setData(List data); void resolveBlocks(); + OS4BlockStateMatcher getBlockMatcher(); } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java index 3a6e4d2..1675052 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java @@ -1,8 +1,9 @@ package com.mcmoddev.orespawn.registries; import net.minecraft.block.Block; +import net.minecraft.block.BlockState; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.extensions.IForgeBlockState; +import net.minecraft.util.registry.DynamicRegistries; import net.minecraftforge.registries.IForgeRegistryModifiable; import net.minecraftforge.registries.RegistryBuilder; @@ -12,6 +13,8 @@ import com.google.common.collect.ImmutableList; import com.mcmoddev.orespawn.data.OS4BlockData; +import com.mcmoddev.orespawn.utils.Helpers; +import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; public class ReplacementsRegistry { private static final IForgeRegistryModifiable replacementsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() @@ -50,7 +53,7 @@ public IReplacementEntry get(final String name) { private class DefaultReplacementEntry implements IReplacementEntry { private ResourceLocation name; private final List data; - private List blockStates; + private List blockStates; DefaultReplacementEntry(ResourceLocation loc, List blocks) { this.name = loc; @@ -78,7 +81,7 @@ public List getReplacementData() { } @Override - public List getReplacementState() { + public List getReplacementState() { return ImmutableList.copyOf(this.blockStates); } @@ -95,9 +98,22 @@ public void setData(List data) { @Override public void resolveBlocks() { - // TODO: Complete - should iterate this.data and use the state deserialize routines - // dereferencing from the blocks list + data.stream() + .map(blockData -> Helpers.deserializeState(String.format("%s[%s]", + blockData.getBlockName(), blockData.getBlockState()))) + .forEach(blockStates::add); } - + + @Override + public OS4BlockStateMatcher getBlockMatcher() { + if (this.blockStates.isEmpty()) return null; + + return new OS4BlockStateMatcher(this.blockStates); + } + } + + public void doDataResolution(DynamicRegistries dynamicRegistries) { + replacementsRegistry.getValues().stream() + .forEach(ent -> ent.resolveBlocks()); } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java index dce9c83..3fe6223 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java @@ -1,5 +1,7 @@ package com.mcmoddev.orespawn.registries; +import net.minecraft.util.registry.DynamicRegistries; + public class SpawnStore { public static void add(String spawnName, Object build) { @@ -7,4 +9,9 @@ public static void add(String spawnName, Object build) { } + public static void doResolveData(DynamicRegistries dynamicRegistries) { + // TODO Auto-generated method stub + + } + } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index 45b2142..8676abe 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -1,7 +1,31 @@ package com.mcmoddev.orespawn.utils; +import com.mcmoddev.orespawn.OreSpawn; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.block.BlockState; +import net.minecraft.command.arguments.BlockStateParser; +import net.minecraft.util.ResourceLocation; + public class Helpers { public static boolean objectNotNull(Object obj) { return obj != null; } + + public static ResourceLocation makeResourceLocation(String blockName) { + if(blockName.contains(":")) return new ResourceLocation(blockName); + else return new ResourceLocation("minecraft", blockName); + } + + public static BlockState deserializeState(final String fullState) { + try { + return new BlockStateParser(new StringReader(fullState), false).parse(false).getState(); + } catch (CommandSyntaxException e) { + OreSpawn.LOGGER.error("Error parsing serialized BlockState {} - {}", fullState, e.getMessage()); + e.printStackTrace(); + return null; + } + } + } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java b/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java new file mode 100644 index 0000000..808f56e --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java @@ -0,0 +1,23 @@ +package com.mcmoddev.orespawn.utils; + +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import net.minecraft.block.BlockState; + +public class OS4BlockStateMatcher { + private final List statesToMatch; + + public OS4BlockStateMatcher(List blockStates) { + statesToMatch = ImmutableList.copyOf(blockStates); + } + + public boolean matches(final BlockState state) { + if (statesToMatch.isEmpty()) return false; + + return statesToMatch.stream().anyMatch(bs -> (bs == state) || + (bs.matchesBlock(state.getBlock()) && + bs.getProperties().containsAll(state.getProperties()))); + } +} From e64a71d7d96db0c550524bfcfca310686d13711c Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Fri, 9 Jul 2021 06:41:28 -0400 Subject: [PATCH 10/37] Bunch of small changes 1) Fill out a stubbed data structure for representing a somewhat data-resolved spawn (this is likely to go away as things get fixed so they expect a late resolve) 2) Move some helper function to Utils.Helpers instead of having them private to a class 3) Implement a "common" Config - nothing for Server or Client in OreSpawn so... --- .../com/mcmoddev/orespawn/data/Config.java | 64 +++++++++++++++++ .../com/mcmoddev/orespawn/data/SpawnData.java | 71 +++++++++++++++++++ .../com/mcmoddev/orespawn/utils/Helpers.java | 34 +++++++-- 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/data/SpawnData.java diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java index 925c426..29b9903 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Config.java @@ -1,6 +1,70 @@ package com.mcmoddev.orespawn.data; +import net.minecraftforge.common.ForgeConfigSpec; +import org.apache.commons.lang3.tuple.Pair; + public class Config { + static + { + final Pair specPair = new ForgeConfigSpec.Builder().configure(CommonConfig::new); + COMMON_SPEC = specPair.getRight(); + COMMON = specPair.getLeft(); + } + + public static final ForgeConfigSpec COMMON_SPEC; + public static final CommonConfig COMMON; + + public static class CommonConfig { + public final ForgeConfigSpec.BooleanValue replaceVanillaOreGeneration; + public final ForgeConfigSpec.BooleanValue replaceAllGeneration; + public final ForgeConfigSpec.BooleanValue enableRetrogeneration; + public final ForgeConfigSpec.BooleanValue forceEnableRetrogeneration; + public final ForgeConfigSpec.BooleanValue flattenBedrock; + public final ForgeConfigSpec.BooleanValue retroactivelyFlattenBedrock; + public final ForgeConfigSpec.IntValue layersOfBedrock; + public final ForgeConfigSpec.BooleanValue extractToDisk; + public final ForgeConfigSpec.BooleanValue ignoreResources; + + CommonConfig(ForgeConfigSpec.Builder builder) { + builder.push("general"); + replaceVanillaOreGeneration = builder + .comment("Attempt to override vanilla Minecraft ore generation when TRUE") + .translation("text.mmd_orespawn.config.replace_vanilla") + .define("Replace Vanilla Oregen", false); + replaceAllGeneration = builder + .comment("Attempt to replace all ore generation, even from other mods, when TRUE") + .translation("text.mmd_orespawn.config.replace_all") + .define("Replace All Generation", false); + enableRetrogeneration = builder + .comment("Attempt to generate new spawns in chunks that were previously generated or were generated with different configuration options when TRUE") + .translation("text.mmd_orespawn.config.retrogen") + .define("Retrogen", false); + forceEnableRetrogeneration = builder + .comment("Force retroactive generation of new spawns, even if the feature is configured to not perform it when TRUE") + .translation("text.mmd_orespawn.config.force_retrogen") + .define("Force Retrogen", false); + flattenBedrock = builder + .comment("Make the bedrock flat in chunks generated when this option is TRUE") + .translation("text.mmd_orespawn.config.flatten_bedrock") + .define("Flatten Bedrock", true); + layersOfBedrock = builder + .comment("How many layers of Bedrock should there be at the bottom of the world? (default 1, max 4)") + .translation("text.mmd_orespawn.config.bedrock_layers") + .defineInRange("Bedrock Thickness", 1, 1, 4); + retroactivelyFlattenBedrock = builder + .comment("Attempt flatten the bedrock in chunks generated before this option and the \"Flatten Bedrock\" option were set to TRUE") + .translation("text.mmd_orespawn.config.retro_bedrock") + .define("Retrogen Flat Bedrock", false); + extractToDisk = builder + .comment("Extract all integration configurations found to \"config/mmd-orespawn-4\" when TRUE") + .translation("text.mmd_orespawn.config.extract_integration") + .define("Extract Files", false); + ignoreResources = builder + .comment("Do not attempt to locate or use any integration configurations when TRUE") + .translation("text.mmd_orespawn.config.ignore_integration") + .define("Ignore Integration", false); + } + } public static void addKnownMod(final String modId) { // TODO: Complete } diff --git a/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java b/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java new file mode 100644 index 0000000..92b4f41 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java @@ -0,0 +1,71 @@ +package com.mcmoddev.orespawn.data; + +import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.api.OS4Feature; +import com.mcmoddev.orespawn.builders.BiomeMatcher; +import com.mcmoddev.orespawn.builders.DimensionMatcher; +import com.mcmoddev.orespawn.registries.IReplacementEntry; +import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; +import net.minecraft.block.BlockState; +import net.minecraft.world.biome.Biome; + +import javax.annotation.Nonnull; +import java.util.List; + +public class SpawnData { + + private final String spawnName; + private final boolean spawnActive; + private final boolean spawnRetrogen; + private final BiomeMatcher spawnBiomes; + private final DimensionMatcher spawnDimensions; + private final OS4BlockStateMatcher replacementMatcher; + private final OS4Feature spawnFeature; + private final List spawnBlocks; + + public SpawnData(@Nonnull final String name, final boolean isSpawnActivated, + final boolean spawnDoesRetrogen, @Nonnull final BiomeMatcher biomes, + @Nonnull final DimensionMatcher dimensions, @Nonnull final IReplacementEntry replacements, + @Nonnull final OS4Feature feature, @Nonnull final List blocks) { + spawnName = name; + spawnActive = isSpawnActivated; + spawnRetrogen = spawnDoesRetrogen; + spawnBiomes = biomes; + spawnDimensions = dimensions; + replacementMatcher = replacements.getBlockMatcher(); + spawnFeature = feature; + spawnBlocks = ImmutableList.copyOf(blocks); + } + + public String getName() { + return spawnName; + } + + public boolean isActive() { + return spawnActive; + } + + public boolean willRetrogen() { + return spawnRetrogen; + } + + public boolean biomeMatch(@Nonnull final Biome biomeIn) { + return spawnBiomes.isAllowed(biomeIn.getRegistryName().toString()); + } + + public boolean dimensionMatch(final String dimensionName) { + return spawnDimensions.isDimensionAllowed(dimensionName); + } + + public boolean canReplace(final BlockState blockStateIn) { + return replacementMatcher.matches(blockStateIn); + } + + public List getBlocks() { + return ImmutableList.copyOf(spawnBlocks); + } + + public OS4Feature getFeature() { + return spawnFeature; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index 8676abe..d0ac55d 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -1,12 +1,19 @@ package com.mcmoddev.orespawn.utils; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.Constants; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.BlockState; import net.minecraft.command.arguments.BlockStateParser; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.nio.file.Paths; public class Helpers { public static boolean objectNotNull(Object obj) { @@ -17,15 +24,34 @@ public static ResourceLocation makeResourceLocation(String blockName) { if(blockName.contains(":")) return new ResourceLocation(blockName); else return new ResourceLocation("minecraft", blockName); } - - public static BlockState deserializeState(final String fullState) { + + public static @Nullable BlockState deserializeState(final String fullState) { + @Nullable BlockState result; try { - return new BlockStateParser(new StringReader(fullState), false).parse(false).getState(); + result = new BlockStateParser(new StringReader(fullState), false).parse(false).getState(); } catch (CommandSyntaxException e) { OreSpawn.LOGGER.error("Error parsing serialized BlockState {} - {}", fullState, e.getMessage()); e.printStackTrace(); - return null; + result = null; + } + return result; + } + public static @Nullable Path makePath(ResourceLocation rl, String type) { + @Nullable Path result; + switch (type) { + case Constants.FileBits.DISK: + result = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, rl.getPath() + ".json"); + break; + case Constants.FileBits.RESOURCE: + ModFile mf = ModList.get().getModFileById(rl.getNamespace()).getFile(); + result = mf.getLocator().findPath(mf, "assets", "orespawn4-data", rl.getPath() + ".json"); + break; + default: + OreSpawn.LOGGER.error("Asked to resolve a path for {} of type {} -- I do not know how to do this", rl.toString(), type); + result = null; + break; } + return result; } } From 481d7d002a62fc3d01168b468cff5ecfd19430ab Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 14 Jul 2021 04:50:14 -0400 Subject: [PATCH 11/37] More Small Changes Updates across the board, implement the config system, start of work on cleaning things up and implementing a proper resource loading and resolving system. --- build.gradle | 5 +- .../java/com/mcmoddev/orespawn/OreSpawn.java | 8 +- .../com/mcmoddev/orespawn/api/OS4API.java | 94 +++++++++++++------ .../orespawn/builders/SpawnBuilder.java | 12 ++- .../com/mcmoddev/orespawn/data/Config.java | 8 +- .../mcmoddev/orespawn/data/OS4BlockData.java | 8 +- .../orespawn/loaders/ResourceLoader.java | 83 ++++++++++------ .../registries/ReplacementsRegistry.java | 37 +++++--- .../orespawn/registries/SpawnStore.java | 13 ++- .../com/mcmoddev/orespawn/utils/Helpers.java | 6 +- 10 files changed, 187 insertions(+), 87 deletions(-) diff --git a/build.gradle b/build.gradle index aa59464..0b486d0 100644 --- a/build.gradle +++ b/build.gradle @@ -512,7 +512,8 @@ repositories { } dependencies { - if (isFG3()) { + implementation 'org.jetbrains:annotations:20.1.0' + if (isFG3()) { minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" } @@ -1202,4 +1203,4 @@ checkerFramework { 'org.checkerframework.checker.nullness.NullnessChecker' ] } -*/ \ No newline at end of file +*/ diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index b5b83a6..bf031c2 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -30,15 +30,17 @@ public OreSpawn() { // Register the doClientStuff method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); // Register the doClientStuff method for modloading - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doServerStartTasks); // Register ourselves for server and other game events we are interested in - MinecraftForge.EVENT_BUS.register(this); + MinecraftForge.EVENT_BUS.addListener(this::doServerStartTasks); + //MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { + OS4API.loadKnownConfigs(); ResourceLoader loader = new ResourceLoader(); loader.runLoaders(); + OS4API.saveKnownConfigs(); } @@ -50,7 +52,7 @@ private void enqueueIMC(final InterModEnqueueEvent event) { private void processIMC(final InterModProcessEvent event) { } - + private void doServerStartTasks(final FMLServerStartingEvent ev) { OS4API.resolveData(ev.getServer().getDynamicRegistries()); } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index 44ca752..882a96f 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -1,22 +1,21 @@ package com.mcmoddev.orespawn.api; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.nio.charset.Charset; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; +import java.nio.file.Paths; import java.util.LinkedList; import java.util.List; -import java.util.Map; - -import org.apache.commons.io.FileUtils; +import com.google.gson.JsonParser; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; +import com.google.gson.Gson; + +import net.minecraft.util.ResourceLocation; +import org.apache.commons.io.FileUtils; + import com.google.gson.stream.JsonReader; import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.builders.SpawnBuilder; @@ -30,17 +29,11 @@ import net.minecraft.util.registry.DynamicRegistries; -public class OS4API { - private static Map modRefs = new HashMap<>(1024); - private static ConfigBlacklist configBlacklist = new ConfigBlacklist(); - - public static void addMod(final String modId, final String modPath) { - modRefs.put(modId, modPath); - } +import static com.mcmoddev.orespawn.utils.Helpers.makeResourceLocation; - public static Map getMods() { - return Collections.unmodifiableMap(modRefs); - } +public class OS4API { + private static final ConfigBlacklist configBlacklist = new ConfigBlacklist(); + private static final List knownConfigs = new LinkedList<>(); /* * Load the `Active Configs` file @@ -82,14 +75,14 @@ public static void loadSpawnsFromStream(InputStream inFile) { OreSpawn.LOGGER.info("Loading JSON version {}", fileVersion); // load the spawns - spawnWrapper.entrySet().stream() + spawnWrapper.entrySet() .forEach( entry -> { String spawnName = entry.getKey(); OreSpawn.LOGGER.info("Loading spawn {} -- {}", spawnName, entry.getValue()); JsonObject spawnData = entry.getValue().getAsJsonObject(); SpawnBuilder theBuilder = new SpawnBuilder(spawnName); - spawnData.entrySet().stream().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); - SpawnStore.add(spawnName, theBuilder.build()); + spawnData.entrySet().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); + SpawnStore.add(theBuilder.build()); }); } @@ -97,10 +90,10 @@ public static void loadSpawnsFromStream(InputStream inFile) { public static void loadPresetsFromStream(InputStream inFile) { JsonObject elements = getParsedJson(inFile).getAsJsonObject(); - elements.entrySet().stream() + elements.entrySet() .forEach( entry -> { String entryZone = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet().stream() + entry.getValue().getAsJsonObject().entrySet() .forEach( subEnt -> { String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); @@ -112,11 +105,11 @@ public static void loadPresetsFromStream(InputStream inFile) { public static void loadReplacementsFromStream(InputStream inFile) { JsonObject elements = getParsedJson(inFile).getAsJsonObject(); - elements.entrySet().stream() + elements.entrySet() .forEach( repl -> { String replName = repl.getKey(); JsonArray replValues = repl.getValue().getAsJsonArray(); - List blocks = new LinkedList(); + List blocks = new LinkedList<>(); for( JsonElement el : replValues) { JsonObject zz = el.getAsJsonObject(); String blName = zz.get("name").getAsString(); @@ -137,14 +130,61 @@ public static void loadFeaturesFromStream(InputStream inFile) { FeaturesRegistry.INSTANCE.addFeature(featureName, className); }); } - + public static boolean isAllowed(final String checkName) { return configBlacklist.isAllowed(checkName); } + public static void loadKnownConfigs() { + Path knownConfigs = Paths.get(Constants.SYSCONF.toAbsolutePath().toString(), "known-configs.json"); + if (knownConfigs.toFile().exists() && knownConfigs.toFile().canRead()) { + JsonParser parser = new JsonParser(); + JsonElement root; + try ( + FileInputStream baseInput = new FileInputStream(knownConfigs.toFile()); + BufferedInputStream dataInput = new BufferedInputStream(baseInput); + InputStreamReader theReader = new InputStreamReader(dataInput)) { + root = parser.parse(new JsonReader(theReader)); + } catch(IOException ex) { + OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); + ex.printStackTrace(); + return; + } + + root.getAsJsonArray().forEach( el -> addModConfig(el.toString())); + } + } + + private static void addModConfig(String itemIn) { + addKnownConfig(makeResourceLocation(itemIn)); + } + + public static void saveKnownConfigs() { + Path knownConfigs = Paths.get(Constants.SYSCONF.toAbsolutePath().toString(), "known-configs.json"); + if (knownConfigs.toFile().canWrite()) { + try (FileOutputStream outStream = new FileOutputStream(knownConfigs.toString()); + OutputStreamWriter writer = new OutputStreamWriter(outStream)) { + Gson gson = new Gson(); + writer.write(gson.toJson(knownConfigs)); + } catch (IOException ex) { + OreSpawn.LOGGER.error("Error saving known configs file: {}", ex.getMessage()); + ex.printStackTrace(); + } + } + } + + public static boolean isKnownConfig(ResourceLocation config) { + return knownConfigs.contains(config); + } + + public static void addKnownConfig(ResourceLocation config) { + knownConfigs.add(config); + } + public static void resolveData(DynamicRegistries dynamicRegistries) { FeaturesRegistry.INSTANCE.doDataResolution(); ReplacementsRegistry.INSTANCE.doDataResolution(dynamicRegistries); SpawnStore.doResolveData(dynamicRegistries); + ReplacementsRegistry.INSTANCE.dump(); } } diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java index fb9d0dc..1fa9092 100644 --- a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java +++ b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java @@ -5,6 +5,7 @@ import com.mcmoddev.orespawn.registries.PresetsRegistry; import com.mcmoddev.orespawn.registries.ReplacementsRegistry; +import java.util.LinkedList; import java.util.List; import com.google.gson.JsonArray; @@ -13,6 +14,7 @@ import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.api.OS4Feature; import com.mcmoddev.orespawn.data.OS4BlockData; +import com.mcmoddev.orespawn.data.SpawnData; import com.mcmoddev.orespawn.data.Constants; public class SpawnBuilder { @@ -28,6 +30,7 @@ public class SpawnBuilder { public SpawnBuilder(final String spawnName) { myName = spawnName; + blocks = new LinkedList<>(); } public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { @@ -55,7 +58,9 @@ public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { case Constants.ConfigNames.BLOCKS: JsonArray blocks = actualData.getAsJsonArray(); blocks.forEach(block -> { - this.addBlock(OS4BlockData.parseJsonData(block)); + OS4BlockData bl = OS4BlockData.parseJsonData(block); + OreSpawn.LOGGER.info("Got Block Data: {} -- {} -- {}", bl.getBlockName(), bl.getBlockState(), bl.getChance()); + this.addBlock(bl); }); break; case Constants.ConfigNames.PARAMETERS: @@ -130,8 +135,7 @@ public DimensionMatcher getDimensions() { return dimensions; } - public Object build() { - // TODO Auto-generated method stub - return null; + public SpawnData build() { + return new SpawnData(myName, active, doRetro, biomes, dimensions, replacements, feature, blocks); } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java index 29b9903..fb2f859 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Config.java @@ -24,6 +24,7 @@ public static class CommonConfig { public final ForgeConfigSpec.IntValue layersOfBedrock; public final ForgeConfigSpec.BooleanValue extractToDisk; public final ForgeConfigSpec.BooleanValue ignoreResources; + public final ForgeConfigSpec.BooleanValue ignoreDisk; CommonConfig(ForgeConfigSpec.Builder builder) { builder.push("general"); @@ -63,9 +64,10 @@ public static class CommonConfig { .comment("Do not attempt to locate or use any integration configurations when TRUE") .translation("text.mmd_orespawn.config.ignore_integration") .define("Ignore Integration", false); + ignoreDisk = builder + .comment("Do not attempt to load any config files that are on disk when TRUE") + .translation("text.mmd_orespawn.config.ignore_disk") + .define("Ignore Config Files On Disk", false); } } - public static void addKnownMod(final String modId) { - // TODO: Complete - } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java index b5e2ff9..59c59f1 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java @@ -2,6 +2,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.mcmoddev.orespawn.OreSpawn; public class OS4BlockData { private final String blockIdentifier; @@ -10,10 +11,10 @@ public class OS4BlockData { public OS4BlockData(final String blockName, final String state, final int chance) { this.blockIdentifier = blockName; - this.blockState = state==null?"":state; + this.blockState = state; this.chance = chance; } - + public OS4BlockData(final String blockName, final String state) { this(blockName, state, 100); } @@ -39,8 +40,9 @@ public static OS4BlockData parseJsonData(JsonElement itemData) { JsonObject actual = itemData.getAsJsonObject(); int chance = actual.has("chance")?actual.get("chance").getAsInt():100; - String state = actual.has("state")?actual.get("state").getAsString():null; + String state = actual.has("state")?actual.get("state").getAsString():""; String name = actual.has("name")?actual.get("name").getAsString():"i-am:a-missing-value"; + OreSpawn.LOGGER.info("actual: {}\nchance: {} :: state: {} :: name: {}", actual.toString(), chance, state, name); return new OS4BlockData(name, state, chance); } diff --git a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java index cd80993..d53692c 100644 --- a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java +++ b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java @@ -11,22 +11,38 @@ import java.util.stream.Collectors; import com.google.common.base.Joiner; + import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.api.OS4API; import com.mcmoddev.orespawn.data.Constants; import com.mcmoddev.orespawn.data.Constants.FileTypes; +import static com.mcmoddev.orespawn.data.Config.COMMON; +import static com.mcmoddev.orespawn.utils.Helpers.makePath; + import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import org.apache.commons.io.FileUtils; + public class ResourceLoader { public void runLoaders() { List foundFiles = new LinkedList<>(); - ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) - .forEach(lrl -> lrl.stream().forEach(rl -> foundFiles.add(rl))); + if (!COMMON.ignoreResources.get()) + ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) + .forEach(foundFiles::addAll); + + if (COMMON.extractToDisk.get()) { + for( ResourceLocation f : foundFiles) { + if (!OS4API.isKnownConfig(f)) { + extractFileToDisk(f); + OS4API.addKnownConfig(f); + } + } + } - iterateDiskFiles().stream().forEach(rl -> foundFiles.add(rl)); + foundFiles.addAll(iterateDiskFiles()); List featuresFiles = foundFiles.stream().filter(rl -> rl.getPath().startsWith("features/")) .collect(Collectors.toList()); @@ -50,36 +66,63 @@ public void runLoaders() { OreSpawn.LOGGER.info("> Spawn Configs:"); spawnConfigs.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); - + if (Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, Constants.FileBits.ALLOWED_MODS).toFile().exists()) { OS4API.loadIntegrationWhitelist(); } - + featuresFiles.stream() .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.FEATURES)); - + replacementsFiles.stream() .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.REPLACEMENTS)); - + presetsFiles.stream() .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.PRESETS)); - + spawnConfigs.stream() .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.SPAWN)); } - + + private void extractFileToDisk(ResourceLocation f) { + Path root = makePath(f, Constants.FileBits.RESOURCE); + Path targetRoot = Constants.JSONPATH.toAbsolutePath(); + + String resourceType = f.getPath().contains("/")?getType(f.getPath()):"config"; + String extractPath = resourceType.equals("config")?f.getNamespace()+"_"+f.getPath().replaceAll("/", "-"): + "config_data/"+resourceType+"/"+f.toString().replaceAll(":", "-").replaceAll("/", "_"); + try { + FileUtils.copyInputStreamToFile(Files.newInputStream(root), Paths.get(targetRoot.toString(), extractPath).toFile()); + } catch (IOException e) { + OreSpawn.LOGGER.error("Exception trying to copy the config {} out to the filesystem as required by the configuration settings: {}", f.toString(), e.getMessage()); + e.printStackTrace(); + } + } + + private String getType(String path) { + String p = path.split("/")[0]; + switch(p) { + case "features": + case "replacements": + case "presets": + return p.substring(0, p.length()-1); // strip the `s` off + default: + return "config"; + } + } + private void loadResourceLocation(final ResourceLocation rl, final FileTypes type) { if (rl.getNamespace().matches("orespawn-disk")) { loadFromDisk(rl, type); } else { loadFromResource(rl, type); - } + } } - + private List iterateFiles(ModFile modFile) { try { Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); @@ -87,7 +130,7 @@ private List iterateFiles(ModFile modFile) { return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds filter(path -> path.toString().endsWith(".json")).map(path -> Joiner.on('/').join(path)) - .map(path -> path.toString()).map(path -> path.substring(0, path.length() - 5)) + .map(path -> path.substring(0, path.length() - 5)) .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) .collect(Collectors.toList()); } catch (IOException e) { @@ -100,7 +143,7 @@ List iterateDiskFiles() { Path diskPath = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4).toAbsolutePath(); return Files.walk(diskPath).map(path -> diskPath.relativize(path.toAbsolutePath())) .filter(path -> path.getNameCount() <= 64).filter(path -> path.toString().endsWith(".json")) - .map(path -> Joiner.on('/').join(path)).map(path -> path.toString()) + .map(path -> Joiner.on('/').join(path)) .map(path -> path.toLowerCase(Locale.US)).map(path -> path.substring(0, path.length() - 5)) .map(path -> new ResourceLocation("orespawn-disk", path)).collect(Collectors.toList()); } catch (IOException e) { @@ -161,18 +204,4 @@ private void loadFromResource(ResourceLocation rl, FileTypes type) { e.printStackTrace(); } } - - private Path makePath(ResourceLocation rl, String type) { - switch(type) { - case Constants.FileBits.DISK: - return Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, rl.getPath()+".json"); - case Constants.FileBits.RESOURCE: - ModFile mf = ModList.get().getModFileById(rl.getNamespace()).getFile(); - return mf.getLocator().findPath(mf, "assets", "orespawn4-data", rl.getPath()+".json"); - default: - OreSpawn.LOGGER.error("Asked to resolve a path for {} of type {} -- I do not know how to do this", rl.toString(), type); - return null; - } - } - } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java index 1675052..956aad2 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java @@ -8,10 +8,12 @@ import net.minecraftforge.registries.RegistryBuilder; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.data.OS4BlockData; import com.mcmoddev.orespawn.utils.Helpers; import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; @@ -25,31 +27,40 @@ public class ReplacementsRegistry { .create(); public static final ReplacementsRegistry INSTANCE = new ReplacementsRegistry(); - + ReplacementsRegistry() { - + } - + public void addReplacement(final String repName, final List data) { if(repName.contains(":")) addReplacement(new ResourceLocation(repName), data); else addReplacement(new ResourceLocation("orespawn", repName), data); } - + public void addReplacement(final ResourceLocation loc, final List data) { DefaultReplacementEntry ent = new DefaultReplacementEntry(loc, data); addReplacement(ent); } - + public void addReplacement(final IReplacementEntry entry) { replacementsRegistry.register(entry); } - + public IReplacementEntry get(final String name) { ResourceLocation loc = name.contains(":")?new ResourceLocation(name):new ResourceLocation("orespawn", name); if (replacementsRegistry.containsKey(loc)) return replacementsRegistry.getValue(loc); else return replacementsRegistry.getValue(new ResourceLocation("orespawn", "default")); } - + + public void dump() { + replacementsRegistry.getValues() + .forEach(entry -> { + OreSpawn.LOGGER.info("Replacement Entry {} -- contains:", entry.getRegistryName()); + entry.getReplacementState() + .forEach(bs -> OreSpawn.LOGGER.info(">> {}", bs.toString())); + }); + } + private class DefaultReplacementEntry implements IReplacementEntry { private ResourceLocation name; private final List data; @@ -58,7 +69,9 @@ private class DefaultReplacementEntry implements IReplacementEntry { DefaultReplacementEntry(ResourceLocation loc, List blocks) { this.name = loc; this.data = blocks; + this.blockStates = new LinkedList<>(); } + @Override public ResourceLocation getRegistryName() { return this.name; @@ -95,25 +108,27 @@ public List getReplacementBlock() { public void setData(List data) { Collections.copy(this.data, data); } - + @Override public void resolveBlocks() { data.stream() - .map(blockData -> Helpers.deserializeState(String.format("%s[%s]", + .filter( bd -> bd != null ) + .map(blockData -> Helpers.deserializeState(String.format("%s[%s]", blockData.getBlockName(), blockData.getBlockState()))) + .filter(bs -> bs != null) .forEach(blockStates::add); } @Override public OS4BlockStateMatcher getBlockMatcher() { if (this.blockStates.isEmpty()) return null; - + return new OS4BlockStateMatcher(this.blockStates); } } public void doDataResolution(DynamicRegistries dynamicRegistries) { - replacementsRegistry.getValues().stream() + replacementsRegistry.getValues() .forEach(ent -> ent.resolveBlocks()); } } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java index 3fe6223..45b2fa4 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java @@ -1,12 +1,17 @@ package com.mcmoddev.orespawn.registries; +import java.util.Map; +import java.util.TreeMap; + +import com.mcmoddev.orespawn.data.SpawnData; + import net.minecraft.util.registry.DynamicRegistries; public class SpawnStore { - - public static void add(String spawnName, Object build) { - // TODO Auto-generated method stub - + private static final Map spawns = new TreeMap<>(); + + public static void add(SpawnData spawnData) { + spawns.put(spawnData.getName(), spawnData); } public static void doResolveData(DynamicRegistries dynamicRegistries) { diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index d0ac55d..6a59d57 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -36,8 +36,8 @@ public static ResourceLocation makeResourceLocation(String blockName) { } return result; } - public static @Nullable Path makePath(ResourceLocation rl, String type) { - @Nullable Path result; + public static Path makePath(ResourceLocation rl, String type) { + Path result; switch (type) { case Constants.FileBits.DISK: result = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, rl.getPath() + ".json"); @@ -48,7 +48,7 @@ public static ResourceLocation makeResourceLocation(String blockName) { break; default: OreSpawn.LOGGER.error("Asked to resolve a path for {} of type {} -- I do not know how to do this", rl.toString(), type); - result = null; + result = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, "error.json"); break; } return result; From ef0dbadf872857a520346041d5c6914ff06ff8aa Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 14 Jul 2021 04:55:40 -0400 Subject: [PATCH 12/37] More small changes (again) Few small fixes and language cleanups here... How to tell IDEA that an inner class that is used a ***LOT*** can't be "static" ? --- .../orespawn/registries/ReplacementsRegistry.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java index 956aad2..f6185a5 100644 --- a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java +++ b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; @@ -64,7 +65,7 @@ public void dump() { private class DefaultReplacementEntry implements IReplacementEntry { private ResourceLocation name; private final List data; - private List blockStates; + private final List blockStates; DefaultReplacementEntry(ResourceLocation loc, List blocks) { this.name = loc; @@ -112,10 +113,10 @@ public void setData(List data) { @Override public void resolveBlocks() { data.stream() - .filter( bd -> bd != null ) + .filter(Objects::nonNull) .map(blockData -> Helpers.deserializeState(String.format("%s[%s]", blockData.getBlockName(), blockData.getBlockState()))) - .filter(bs -> bs != null) + .filter(Objects::nonNull) .forEach(blockStates::add); } @@ -127,8 +128,8 @@ public OS4BlockStateMatcher getBlockMatcher() { } } - public void doDataResolution(DynamicRegistries dynamicRegistries) { + public void doDataResolution(@SuppressWarnings("unused") DynamicRegistries dynamicRegistries) { replacementsRegistry.getValues() - .forEach(ent -> ent.resolveBlocks()); + .forEach(IReplacementEntry::resolveBlocks); } } From 3af3e1ba6757bdfa23f5c8599379e57cd004e579 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 14 Jul 2021 18:44:22 -0400 Subject: [PATCH 13/37] About halfway towards having data ready for proper resolution steps --- .../com/mcmoddev/orespawn/api/OS4API.java | 165 +++++++++--------- .../com/mcmoddev/orespawn/api/OS4Feature.java | 5 - .../com/mcmoddev/orespawn/api/OS4Plugin.java | 24 --- .../orespawn/builders/BiomeMatcher.java | 64 ------- .../orespawn/builders/DimensionMatcher.java | 43 ----- .../orespawn/builders/SpawnBuilder.java | 141 --------------- .../com/mcmoddev/orespawn/data/BlockData.java | 63 +++++++ .../orespawn/data/ConfigBlacklist.java | 14 +- .../com/mcmoddev/orespawn/data/Constants.java | 4 +- .../mcmoddev/orespawn/data/FeaturesStore.java | 15 ++ .../mcmoddev/orespawn/data/OS4BlockData.java | 49 ------ .../mcmoddev/orespawn/data/PresetsStore.java | 73 ++++++++ .../com/mcmoddev/orespawn/data/SpawnData.java | 71 -------- .../mcmoddev/orespawn/data/SpawnStore.java | 137 +++++++++++++++ .../orespawn/loaders/ResourceLoader.java | 9 +- .../orespawn/registries/FeaturesRegistry.java | 108 ------------ .../orespawn/registries/IFeatureEntry.java | 12 -- .../orespawn/registries/IPresetEntry.java | 10 -- .../registries/IReplacementEntry.java | 19 -- .../orespawn/registries/PresetsRegistry.java | 75 -------- .../registries/ReplacementsRegistry.java | 135 -------------- .../orespawn/registries/SpawnStore.java | 22 --- .../com/mcmoddev/orespawn/utils/Helpers.java | 40 ++++- .../orespawn/utils/OS4BlockStateMatcher.java | 23 --- 24 files changed, 426 insertions(+), 895 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/BlockData.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/SpawnData.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index 882a96f..c3f05f2 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -6,6 +6,7 @@ import java.nio.file.Paths; import java.util.LinkedList; import java.util.List; +import java.util.Map; import com.google.gson.JsonParser; import com.google.gson.JsonArray; @@ -13,23 +14,17 @@ import com.google.gson.JsonObject; import com.google.gson.Gson; +import com.mcmoddev.orespawn.data.*; import net.minecraft.util.ResourceLocation; import org.apache.commons.io.FileUtils; import com.google.gson.stream.JsonReader; import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.builders.SpawnBuilder; -import com.mcmoddev.orespawn.data.ConfigBlacklist; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.OS4BlockData; -import com.mcmoddev.orespawn.registries.FeaturesRegistry; -import com.mcmoddev.orespawn.registries.PresetsRegistry; -import com.mcmoddev.orespawn.registries.ReplacementsRegistry; -import com.mcmoddev.orespawn.registries.SpawnStore; import net.minecraft.util.registry.DynamicRegistries; +import org.apache.commons.lang3.tuple.Pair; -import static com.mcmoddev.orespawn.utils.Helpers.makeResourceLocation; +import static com.mcmoddev.orespawn.utils.Helpers.makeInternalResourceLocation; public class OS4API { private static final ConfigBlacklist configBlacklist = new ConfigBlacklist(); @@ -54,9 +49,11 @@ public static void loadIntegrationWhitelist() { JsonObject topLevel = parser.parse(rawJson).getAsJsonObject(); if (topLevel.has("mods")) - topLevel.get("mods").getAsJsonArray().forEach(modid -> configBlacklist.addMod(modid.getAsString())); + for (JsonElement el : topLevel.get("mods").getAsJsonArray()) + configBlacklist.addMod(el.getAsString()); if (topLevel.has("configs")) - topLevel.get("configs").getAsJsonArray().forEach(configFile -> configBlacklist.addConfig(configFile.getAsString())); + for (JsonElement el : topLevel.get("configs").getAsJsonArray()) + configBlacklist.addConfig(el.getAsString()); } private static JsonElement getParsedJson(InputStream stream) { @@ -66,69 +63,11 @@ private static JsonElement getParsedJson(InputStream stream) { return parserToReturnFrom.parse(jsonReader); } - public static void loadSpawnsFromStream(InputStream inFile) { - JsonObject rawJson = getParsedJson(inFile).getAsJsonObject(); - - String fileVersion = rawJson.get(Constants.ConfigNames.VERSION).getAsString(); - JsonObject spawnWrapper = rawJson.get(Constants.ConfigNames.SPAWNS).getAsJsonObject(); - - OreSpawn.LOGGER.info("Loading JSON version {}", fileVersion); - - // load the spawns - spawnWrapper.entrySet() - .forEach( entry -> { - String spawnName = entry.getKey(); - OreSpawn.LOGGER.info("Loading spawn {} -- {}", spawnName, entry.getValue()); - JsonObject spawnData = entry.getValue().getAsJsonObject(); - SpawnBuilder theBuilder = new SpawnBuilder(spawnName); - spawnData.entrySet().forEach(spawn -> theBuilder.addItem(spawn.getKey(), spawn.getValue())); - SpawnStore.add(theBuilder.build()); - }); - - } - public static void loadPresetsFromStream(InputStream inFile) { JsonObject elements = getParsedJson(inFile).getAsJsonObject(); - elements.entrySet() - .forEach( entry -> { - String entryZone = entry.getKey(); - entry.getValue().getAsJsonObject().entrySet() - .forEach( subEnt -> { - String entryName = String.format("%s.%s", entryZone, subEnt.getKey()); - PresetsRegistry.INSTANCE.addPreset(entryName, subEnt.getValue()); - }); - - }); - } - - public static void loadReplacementsFromStream(InputStream inFile) { - JsonObject elements = getParsedJson(inFile).getAsJsonObject(); - - elements.entrySet() - .forEach( repl -> { - String replName = repl.getKey(); - JsonArray replValues = repl.getValue().getAsJsonArray(); - List blocks = new LinkedList<>(); - for( JsonElement el : replValues) { - JsonObject zz = el.getAsJsonObject(); - String blName = zz.get("name").getAsString(); - String state = zz.has("state")?zz.get("state").getAsString():""; - blocks.add(new OS4BlockData(blName, state)); - } - ReplacementsRegistry.INSTANCE.addReplacement(replName, blocks); - }); - } - - public static void loadFeaturesFromStream(InputStream inFile) { - JsonArray elements = getParsedJson(inFile).getAsJsonArray(); - - elements.forEach( ent -> { - JsonObject entry = ent.getAsJsonObject(); - String featureName = entry.get("name").getAsString(); - String className = entry.get("class").getAsString(); - FeaturesRegistry.INSTANCE.addFeature(featureName, className); - }); + // let the storage actually do the load - it can do the recursion necessary to get the name and all that + PresetsStore.load(elements); } public static boolean isAllowed(final String checkName) { @@ -155,8 +94,8 @@ public static void loadKnownConfigs() { } } - private static void addModConfig(String itemIn) { - addKnownConfig(makeResourceLocation(itemIn)); + private static void addModConfig(final String itemIn) { + addKnownConfig(makeInternalResourceLocation(itemIn)); } public static void saveKnownConfigs() { @@ -181,10 +120,80 @@ public static void addKnownConfig(ResourceLocation config) { knownConfigs.add(config); } - public static void resolveData(DynamicRegistries dynamicRegistries) { - FeaturesRegistry.INSTANCE.doDataResolution(); - ReplacementsRegistry.INSTANCE.doDataResolution(dynamicRegistries); - SpawnStore.doResolveData(dynamicRegistries); - ReplacementsRegistry.INSTANCE.dump(); + public static void resolveBlockData() { + // TODO: We get called from the proper event to resolve our data + // TODO: This means we get called at the start of RegistryEvent.Register, hopefully... + } + + public static void resolveWorldData(DynamicRegistries dynamicRegistries) { + // TODO: We get called from the proper event to resolve our data + // TODO: This means we get called at the start of ServerStarting, hopefully... + } + + public static void loadFeaturesFromStream(InputStream newInputStream) { + /* + { + "feature name" : "feature classpath", + ... + } + */ + JsonObject featuresIn = getParsedJson(newInputStream).getAsJsonObject(); + + featuresIn.entrySet().stream() + .map( baseEntry -> Pair.of(baseEntry.getKey(), baseEntry.getValue().getAsString()) ) + .forEach( manipulated -> FeaturesStore.addNewFeature( manipulated.getKey(), manipulated.getValue())); + } + + public static void loadReplacementsFromStream(InputStream newInputStream) { + /* + { + "replacement name" : [ { block data }, ... ], + ... + } + */ + JsonObject replacementsIn = getParsedJson(newInputStream).getAsJsonObject(); + + for ( Map.Entry x : replacementsIn.entrySet()) { + String entryName = x.getKey(); + JsonArray entries = x.getValue().getAsJsonArray(); + List blocks = new LinkedList<>(); + entries.forEach( entry -> blocks.add(BlockData.makeFromJson(entry.getAsJsonObject()))); + } + } + + public static void loadSpawnsFromStream(InputStream newInputStream) { + /* + { + "version": 2.0, + "spawns": { + "spawn name": { + "feature": "feature name or $.preset", + "replacements": "replacement name or $.preset or array", + "dimensions": array - whitelist, with special values or $.preset, + "biomes": object of whitelist/blacklist segments or $.preset, + "blocks": array of block data or $.preset, + "parameters": variable object or $.preset + }, + ... + } + } + */ + JsonObject spawnFileData = getParsedJson(newInputStream).getAsJsonObject(); + + if (!spawnFileData.has("version") || + (spawnFileData.has("version") && spawnFileData.get("version").getAsFloat() != 2.0)) { + OreSpawn.LOGGER.error("Spawn File does not have a version or is of an unhandled or unknown version. Ignoring."); + return; + } + if (!spawnFileData.has("spawns")) { + OreSpawn.LOGGER.error("Spawn File of incorrect format or has no spawns, ignoring."); + return; + } + + OreSpawn.LOGGER.info("Loading Spawns, version {}", spawnFileData.get("version").getAsFloat()); + + JsonObject rawSpawnData = spawnFileData.get("spawns").getAsJsonObject(); + + rawSpawnData.entrySet().forEach( entry -> SpawnStore.loadFromJson(entry)); } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java deleted file mode 100644 index 29ee672..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.mcmoddev.orespawn.api; - -public class OS4Feature { - -} diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java deleted file mode 100644 index c9ceb17..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4Plugin.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Retention(RUNTIME) -@Target(TYPE) -public @interface OS4Plugin { - - // the Mod this is for - will be used for - // generating the name of the json the config - // will get saved to and should also be the - // actual mod-id we can use for creating a - // resource location - String modid(); - - // resource location segment to look in - // for registered config files - - String resourcePath() default "orespawn"; -} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java deleted file mode 100644 index db5e754..0000000 --- a/src/main/java/com/mcmoddev/orespawn/builders/BiomeMatcher.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.mcmoddev.orespawn.builders; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.biome.Biome; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -public class BiomeMatcher { - private final List> allowedBiomes = new LinkedList<>(); - private final List> blockedBiomes = new LinkedList<>(); - private final List rawBiomeWhitelist = new LinkedList<>(); - private final List rawBiomeBlacklist = new LinkedList<>(); - private boolean resolved = false; - - public BiomeMatcher(JsonObject rawData) { - if (rawData.has("whitelist")) - loadList(rawData.get("whitelist").getAsJsonArray(), rawBiomeWhitelist); - if (rawData.has("blacklist")) - loadList(rawData.get("blacklist").getAsJsonArray(), rawBiomeBlacklist); - } - - private void loadList(JsonArray inputData, List theList) { - inputData.forEach(elem -> theList.add(elem.getAsString())); - } - - public void resolve() { - if (!rawBiomeWhitelist.isEmpty()) - allowedBiomes.addAll( rawBiomeWhitelist.stream() - .map( name -> getRegistryKeyFor(name) ) - .collect(Collectors.toList())); - if (!rawBiomeBlacklist.isEmpty()) - blockedBiomes.addAll( rawBiomeBlacklist.stream() - .map( name -> getRegistryKeyFor(name) ) - .collect(Collectors.toList())); - - resolved = true; - } - - private RegistryKey getRegistryKeyFor(String name) { - return RegistryKey.getOrCreateKey(Registry.BIOME_KEY, getResourceLocationFor(name)); - } - - private ResourceLocation getResourceLocationFor(String name) { - String mod = name.contains(":")?name.split(":")[0]:"minecraft"; - String lname = name.contains(":")?name.split(":")[1]:name; - return new ResourceLocation(mod, lname); - } - - public boolean isAllowed(final String name) { - if (!resolved) return true; - if (allowedBiomes.isEmpty() && blockedBiomes.isEmpty()) return true; - - - RegistryKey key = getRegistryKeyFor(name); - return (allowedBiomes.contains(key) && !blockedBiomes.contains(key)); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java deleted file mode 100644 index 19166db..0000000 --- a/src/main/java/com/mcmoddev/orespawn/builders/DimensionMatcher.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.mcmoddev.orespawn.builders; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import com.google.gson.JsonArray; - -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.Dimension; - -public class DimensionMatcher { - private final List> allowedDimensions; - private final List rawDimensions; - private boolean loaded = false; - - public DimensionMatcher(JsonArray dimensionData) { - allowedDimensions = new LinkedList<>(); - rawDimensions = new LinkedList<>(); - dimensionData.forEach(dim -> rawDimensions.add(dim.getAsString())); - } - - private RegistryKey getKeyFromString(final String dimensionName) { - String mod = dimensionName.contains(":")?dimensionName.split(":")[0]:"minecraft"; - String name = dimensionName.contains(":")?dimensionName.split(":")[1]:dimensionName; - ResourceLocation loc = new ResourceLocation(mod, name); - return RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, loc); - } - public boolean isDimensionAllowed(final String dimension) { - if (allowedDimensions.isEmpty() || !loaded) return true; - return allowedDimensions.contains(getKeyFromString(dimension)); - } - - public void resolveData() { - allowedDimensions.addAll( - rawDimensions.stream() - .map( rawDim -> getKeyFromString(rawDim) ) - .collect(Collectors.toList())); - loaded = true; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java b/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java deleted file mode 100644 index 1fa9092..0000000 --- a/src/main/java/com/mcmoddev/orespawn/builders/SpawnBuilder.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.mcmoddev.orespawn.builders; - -import com.mcmoddev.orespawn.registries.FeaturesRegistry; -import com.mcmoddev.orespawn.registries.IReplacementEntry; -import com.mcmoddev.orespawn.registries.PresetsRegistry; -import com.mcmoddev.orespawn.registries.ReplacementsRegistry; - -import java.util.LinkedList; -import java.util.List; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4Feature; -import com.mcmoddev.orespawn.data.OS4BlockData; -import com.mcmoddev.orespawn.data.SpawnData; -import com.mcmoddev.orespawn.data.Constants; - -public class SpawnBuilder { - private String myName; - private boolean doRetro; - private boolean active; - private DimensionMatcher dimensions; - private IReplacementEntry replacements; - private BiomeMatcher biomes; - private List blocks; - private JsonObject parameters; - private OS4Feature feature; - - public SpawnBuilder(final String spawnName) { - myName = spawnName; - blocks = new LinkedList<>(); - } - - public SpawnBuilder addItem(final String itemName, final JsonElement itemData) { - JsonElement actualData = itemData; - if (itemData.isJsonPrimitive()) { - if (itemData.getAsString().startsWith("$")) { - // its a preset, lets mangle it in... - actualData = PresetsRegistry.INSTANCE.getPreset(itemData.getAsString().substring(1)); - } - } - - switch(itemName) { - case Constants.ConfigNames.REPLACEMENT: - this.setReplacement(actualData.getAsString()); - break; - case Constants.ConfigNames.FEATURE: - this.setFeature(actualData.getAsString()); - break; - case Constants.ConfigNames.DIMENSIONS: - this.setDimensions(new DimensionMatcher(actualData.getAsJsonArray())); - break; - case Constants.ConfigNames.BIOMES: - this.setBiomes(new BiomeMatcher(actualData.getAsJsonObject())); - break; - case Constants.ConfigNames.BLOCKS: - JsonArray blocks = actualData.getAsJsonArray(); - blocks.forEach(block -> { - OS4BlockData bl = OS4BlockData.parseJsonData(block); - OreSpawn.LOGGER.info("Got Block Data: {} -- {} -- {}", bl.getBlockName(), bl.getBlockState(), bl.getChance()); - this.addBlock(bl); - }); - break; - case Constants.ConfigNames.PARAMETERS: - this.parameters = actualData.getAsJsonObject(); - break; - case Constants.ConfigNames.RETROGEN: - this.doRetro = actualData.getAsBoolean(); - break; - case Constants.ConfigNames.ENABLED: - this.active = actualData.getAsBoolean(); - break; - default: - OreSpawn.LOGGER.error("Unknown config item {} - skipping", itemName); - } - - return this; - } - - private void addBlock(OS4BlockData block) { - blocks.add(block); - } - - private void setBiomes(BiomeMatcher biomeMatcher) { - biomes = biomeMatcher; - } - - private void setDimensions(DimensionMatcher dimensionMatcher) { - dimensions = dimensionMatcher; - } - - private void setReplacement(final String replacementName) { - replacements = ReplacementsRegistry.INSTANCE.get(replacementName); - } - - private void setFeature(final String featureName) { - feature = FeaturesRegistry.INSTANCE.getFeature(featureName); - } - - public JsonObject getParameters() { - return parameters; - } - - public List getBlocks() { - return blocks; - } - - public OS4Feature getFeature() { - return feature; - } - - public String getMyName() { - return myName; - } - - public boolean isActive() { - return active; - } - - public boolean isRetrogen() { - return doRetro; - } - - public BiomeMatcher getBiomes() { - return biomes; - } - - public IReplacementEntry getReplacements() { - return replacements; - } - - public DimensionMatcher getDimensions() { - return dimensions; - } - - public SpawnData build() { - return new SpawnData(myName, active, doRetro, biomes, dimensions, replacements, feature, blocks); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/BlockData.java new file mode 100644 index 0000000..6c1d916 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/BlockData.java @@ -0,0 +1,63 @@ +package com.mcmoddev.orespawn.data; + +import com.google.gson.JsonObject; + +public class BlockData { + private final String blockName; + private final String blockState; + private final boolean hasState; + private final int chanceToAppear; + + public BlockData(final String name, final String state, final int chance) { + blockName = name; + blockState = state; + hasState = true; + chanceToAppear = chance; + } + + public BlockData(final String name, final String state) { + blockName = name; + blockState = state; + hasState = true; + chanceToAppear = -1; + } + + public BlockData(final String name, final int chance) { + blockName = name; + chanceToAppear = chance; + hasState = false; + blockState = ""; + } + + public BlockData(final String name) { + blockName = name; + chanceToAppear = -1; + hasState = false; + blockState = ""; + } + + public String getBlockWithState() { + return hasState?String.format("%s[%s]", blockName, blockState):blockName; + } + + public int getChance() { + return chanceToAppear; + } + + public static BlockData makeFromJson(JsonObject dataIn) { + if (dataIn.has("state")) { + if (dataIn.has("chance")) { + return new BlockData(dataIn.get("name").getAsString(), + dataIn.get("state").getAsString(), dataIn.get("chance").getAsInt()); + } else { + return new BlockData(dataIn.get("name").getAsString(), dataIn.get("state").getAsString()); + } + } else { + if (dataIn.has("chance")) { + return new BlockData(dataIn.get("name").getAsString(), dataIn.get("chance").getAsInt()); + } else { + return new BlockData(dataIn.get("name").getAsString()); + } + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java b/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java index 36cfa43..0676dc8 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java +++ b/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java @@ -1,28 +1,30 @@ package com.mcmoddev.orespawn.data; +import com.google.gson.JsonElement; + import java.util.List; import java.util.LinkedList; public class ConfigBlacklist { private static final List blockedMods = new LinkedList<>(); private static final List blockedConfigs = new LinkedList<>(); - + public ConfigBlacklist() { - + } - + public void addMod(final String modid) { blockedMods.add(modid); } - + public void addConfig(final String configName) { blockedConfigs.add(configName); } - + public boolean isAllowed(final String checkName) { return !(blockedMods.contains(checkName) || blockedConfigs.contains(checkName)); } - + public boolean isBlocked(final String checkName) { return !isAllowed(checkName); } diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index c33514d..cb24c49 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -31,7 +31,7 @@ public static class FileBits { public static final String RESOURCE = "__RESOURCE__"; public static final String RESOURCE_PATH = "/assets/orespawn-data"; } - public static enum FileTypes { - FEATURES, SPAWN, PRESETS, REPLACEMENTS; + public enum FileTypes { + FEATURES, SPAWN, PRESETS, REPLACEMENTS } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java b/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java new file mode 100644 index 0000000..c425d5e --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java @@ -0,0 +1,15 @@ +package com.mcmoddev.orespawn.data; + +import com.mcmoddev.orespawn.OreSpawn; + +import java.util.HashMap; +import java.util.Map; + +public class FeaturesStore { + private static Map featuresList = new HashMap<>(1024); + + public static void addNewFeature(String key, String value) { + if (!featuresList.containsKey(key)) featuresList.put(key, value); + else OreSpawn.LOGGER.error("Duplicate feature name {} with value {} found during load, ignoring.", key, value); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java deleted file mode 100644 index 59c59f1..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4BlockData.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; - -public class OS4BlockData { - private final String blockIdentifier; - private final String blockState; - private final int chance; - - public OS4BlockData(final String blockName, final String state, final int chance) { - this.blockIdentifier = blockName; - this.blockState = state; - this.chance = chance; - } - - public OS4BlockData(final String blockName, final String state) { - this(blockName, state, 100); - } - - public final int getChance() { - return this.chance; - } - - public final String getBlock() { - String fmt = this.blockState.length()==0?"%s%s":"%s[%s]"; - return String.format(fmt, this.blockIdentifier, this.blockState); - } - - public final String getBlockName() { - return this.blockIdentifier; - } - - public final String getBlockState() { - return this.blockState; - } - - public static OS4BlockData parseJsonData(JsonElement itemData) { - JsonObject actual = itemData.getAsJsonObject(); - - int chance = actual.has("chance")?actual.get("chance").getAsInt():100; - String state = actual.has("state")?actual.get("state").getAsString():""; - String name = actual.has("name")?actual.get("name").getAsString():"i-am:a-missing-value"; - OreSpawn.LOGGER.info("actual: {}\nchance: {} :: state: {} :: name: {}", actual.toString(), chance, state, name); - - return new OS4BlockData(name, state, chance); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java b/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java new file mode 100644 index 0000000..515980b --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java @@ -0,0 +1,73 @@ +package com.mcmoddev.orespawn.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.util.HashMap; +import java.util.Map; + +public class PresetsStore { + private static final Map presets = new HashMap<>(1024); + + /* + Presets File Format: + { + "section name": { + "item name": value_as_needed_for_section + }, + ... + } + + Sections: feature, replacement, parameter, dimension, biome + */ + + public static void load(final JsonObject presetsIn) { + presetsIn.entrySet().forEach(entry -> { + entry.getValue().getAsJsonObject().entrySet().forEach( + presetEntry -> { + final String fullName = String.format("$.%s.%s", entry.getKey(), presetEntry.getKey()); + presets.put(fullName, loadPresetDispatch(entry.getKey(), presetEntry.getValue())); + }); + }); + } + + private static JsonElement loadPresetDispatch(final String sectionName, final JsonElement value) { + // TODO: sectionName should be used to properly handle the section loading + return value; + } + + public static JsonElement getPreset(final String presetName) { + String lookupName = presetName.startsWith("$.")?presetName:String.format("$.%s", presetName); + if (presets.containsKey(lookupName)) return presets.get(lookupName); + return new JsonArray(); + } + + public static String interpolateString(JsonElement value) { + if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { + String getMe = value.getAsString(); + return presets.containsKey(getMe)?getPreset(getMe).getAsString():getMe; + } + + return value.getAsString(); + } + + public static JsonArray interpolateArray(JsonElement value) { + if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { + String getMe = value.getAsString(); + return getPreset(getMe).getAsJsonArray(); + } + + return value.getAsJsonArray(); + } + + public static JsonObject interpolateObject(JsonElement value) { + if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { + String getMe = value.getAsString(); + JsonElement preset = getPreset(getMe); + return preset.isJsonObject()?preset.getAsJsonObject():new JsonObject(); + } + + return value.getAsJsonObject(); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java b/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java deleted file mode 100644 index 92b4f41..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/SpawnData.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.common.collect.ImmutableList; -import com.mcmoddev.orespawn.api.OS4Feature; -import com.mcmoddev.orespawn.builders.BiomeMatcher; -import com.mcmoddev.orespawn.builders.DimensionMatcher; -import com.mcmoddev.orespawn.registries.IReplacementEntry; -import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; -import net.minecraft.block.BlockState; -import net.minecraft.world.biome.Biome; - -import javax.annotation.Nonnull; -import java.util.List; - -public class SpawnData { - - private final String spawnName; - private final boolean spawnActive; - private final boolean spawnRetrogen; - private final BiomeMatcher spawnBiomes; - private final DimensionMatcher spawnDimensions; - private final OS4BlockStateMatcher replacementMatcher; - private final OS4Feature spawnFeature; - private final List spawnBlocks; - - public SpawnData(@Nonnull final String name, final boolean isSpawnActivated, - final boolean spawnDoesRetrogen, @Nonnull final BiomeMatcher biomes, - @Nonnull final DimensionMatcher dimensions, @Nonnull final IReplacementEntry replacements, - @Nonnull final OS4Feature feature, @Nonnull final List blocks) { - spawnName = name; - spawnActive = isSpawnActivated; - spawnRetrogen = spawnDoesRetrogen; - spawnBiomes = biomes; - spawnDimensions = dimensions; - replacementMatcher = replacements.getBlockMatcher(); - spawnFeature = feature; - spawnBlocks = ImmutableList.copyOf(blocks); - } - - public String getName() { - return spawnName; - } - - public boolean isActive() { - return spawnActive; - } - - public boolean willRetrogen() { - return spawnRetrogen; - } - - public boolean biomeMatch(@Nonnull final Biome biomeIn) { - return spawnBiomes.isAllowed(biomeIn.getRegistryName().toString()); - } - - public boolean dimensionMatch(final String dimensionName) { - return spawnDimensions.isDimensionAllowed(dimensionName); - } - - public boolean canReplace(final BlockState blockStateIn) { - return replacementMatcher.matches(blockStateIn); - } - - public List getBlocks() { - return ImmutableList.copyOf(spawnBlocks); - } - - public OS4Feature getFeature() { - return spawnFeature; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java new file mode 100644 index 0000000..c6aecbc --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java @@ -0,0 +1,137 @@ +package com.mcmoddev.orespawn.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mcmoddev.orespawn.OreSpawn; + +import java.util.*; + +public class SpawnStore { + private static final Map spawns = new TreeMap<>(); + + public static void loadFromJson(final Map.Entry dataIn) { + SpawnData newSpawn = new SpawnData(); + dataIn.getValue().getAsJsonObject().entrySet().forEach(baseEntry -> { + newSpawn.setName(baseEntry.getKey()); + baseEntry.getValue().getAsJsonObject().entrySet().forEach(entry -> { + switch (entry.getKey()) { + case "feature": + newSpawn.setFeatureName(PresetsStore.interpolateString(entry.getValue())); + break; + case "replaces": + newSpawn.setReplacementName(PresetsStore.interpolateString(entry.getValue())); + break; + case "dimensions": + JsonArray midpoint = PresetsStore.interpolateArray(entry.getValue()); + for (JsonElement jsonElement : midpoint) { + String dimName = jsonElement.getAsString(); + newSpawn.addDimension(dimName); + } + break; + case "biomes": + JsonObject baseData = PresetsStore.interpolateObject(entry.getValue()); + for (Map.Entry ent : baseData.entrySet()) { + if (ent.getKey().matches("whitelist")) { + ent.getValue().getAsJsonArray().forEach(item -> newSpawn.addBiomeToWhitelist(item.getAsString())); + } else if (ent.getKey().matches("blacklist")) { + ent.getValue().getAsJsonArray().forEach(item -> newSpawn.addBiomeToBlacklist(item.getAsString())); + } + } + break; + case "parameters": + newSpawn.setParameters(entry.getValue().getAsJsonObject()); + break; + case "blocks": + JsonArray blockData = PresetsStore.interpolateArray(entry.getValue()); + blockData.forEach( block -> newSpawn.addBlock(BlockData.makeFromJson(block.getAsJsonObject()))); + break; + default: + OreSpawn.LOGGER.error("Unkown entry {} in spawn {}", entry.getKey(), baseEntry.getKey()); + } + }); + }); + } + + public static class SpawnData { + private String featureName; + private String replacementName; + private List dimensionList; + private List biomeWhitelist; + private List biomeBlacklist; + private JsonObject parameters; + private String spawnName; + private List blocks; + + public SpawnData() { + dimensionList = new LinkedList<>(); + biomeWhitelist = new LinkedList<>(); + biomeBlacklist = new LinkedList<>(); + blocks = new LinkedList<>(); + } + + public SpawnData setFeatureName(final String name) { + featureName = name; + return this; + } + + public SpawnData setReplacementName(final String name) { + replacementName = name; + return this; + } + + public SpawnData addDimension(final String dimensionName) { + dimensionList.add(dimensionName); + return this; + } + + public SpawnData setBiomesAll() { + biomeWhitelist = Collections.emptyList(); + biomeBlacklist = Arrays.asList("orespawn4:any"); + return this; + } + + public SpawnData addBiomeToWhitelist(final String biomeName) { + biomeWhitelist.add(biomeName); + return this; + } + + public SpawnData addBiomeToBlacklist(final String biomeName) { + biomeBlacklist.add(biomeName); + return this; + } + + public SpawnData setAllOverworld() { + dimensionList = Arrays.asList("orespawn4:overworlds"); + return this; + } + + public SpawnData setAllNether() { + dimensionList = Arrays.asList("orespawn4:nethers"); + + return this; + } + + public SpawnData setAllVoidOrEnd() { + dimensionList = Arrays.asList("orespawn4:ends"); + + return this; + } + + public SpawnData setParameters(final JsonObject parametersElement) { + parameters = parametersElement; + + return this; + } + + public SpawnData setName(final String name) { + spawnName = name; + return this; + } + + public SpawnData addBlock(final BlockData block) { + blocks.add(block); + return this; + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java index d53692c..08cf048 100644 --- a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java +++ b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java @@ -18,8 +18,9 @@ import com.mcmoddev.orespawn.data.Constants.FileTypes; import static com.mcmoddev.orespawn.data.Config.COMMON; -import static com.mcmoddev.orespawn.utils.Helpers.makePath; +import static com.mcmoddev.orespawn.utils.Helpers.*; +import com.mcmoddev.orespawn.utils.Helpers; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.loading.moddiscovery.ModFile; @@ -144,8 +145,10 @@ List iterateDiskFiles() { return Files.walk(diskPath).map(path -> diskPath.relativize(path.toAbsolutePath())) .filter(path -> path.getNameCount() <= 64).filter(path -> path.toString().endsWith(".json")) .map(path -> Joiner.on('/').join(path)) - .map(path -> path.toLowerCase(Locale.US)).map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation("orespawn-disk", path)).collect(Collectors.toList()); + .map(path -> path.toLowerCase(Locale.US)) + .map(path -> path.substring(0, path.length() - 5)) + .map(path -> String.format("orespawn-disk:%s",path)) + .map(Helpers::makeInternalResourceLocation).collect(Collectors.toList()); } catch (IOException e) { return Collections.emptyList(); } diff --git a/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java deleted file mode 100644 index b421d2c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/FeaturesRegistry.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import java.lang.reflect.InvocationTargetException; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4Feature; - -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.registries.IForgeRegistryModifiable; -import net.minecraftforge.registries.RegistryBuilder; - -public class FeaturesRegistry { - private static final IForgeRegistryModifiable featuresRegistry = (IForgeRegistryModifiable) new RegistryBuilder() - .setName(new ResourceLocation("orespawn", "featuress_registry")) - .allowModification() - .setType(IFeatureEntry.class) - .setMaxID(Integer.MAX_VALUE) - .create(); - - public static final FeaturesRegistry INSTANCE = new FeaturesRegistry(); - - FeaturesRegistry() { - - } - - public void addFeature(final DefaultFeatureEntry feature) { - if ( feature.getClass() == null) return; - featuresRegistry.register(feature); - } - - public void addFeature(final ResourceLocation loc, final String featureClass) { - this.addFeature(new DefaultFeatureEntry(loc, featureClass)); - } - - public void addFeature(final String entryName, final String featureClass) { - String resDom = entryName.contains(":")?entryName.split(":")[0]:"orespawn"; - String resPath = entryName.contains(":")?entryName.split(":")[1]:entryName; - this.addFeature(new ResourceLocation(resDom, resPath), featureClass); - } - - public OS4Feature getFeature(final String featureName) { - IFeatureEntry feature = featuresRegistry.getValue(featureName.contains(":")?new ResourceLocation(featureName):new ResourceLocation("orespawn",featureName)); - try { - return (OS4Feature) feature.getClass().getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException e) { - OreSpawn.LOGGER.fatal(e.getMessage()); - return null; - } - } - - private class DefaultFeatureEntry implements IFeatureEntry { - private ResourceLocation resLoc; - private String featureClassPath; - private Class featureClass; - - public DefaultFeatureEntry(final ResourceLocation location, final String classPath) { - this.resLoc = location; - this.featureClassPath = classPath; - } - - public Class getFeature() { - return this.featureClass; - } - - public String getClassName() { - return this.featureClassPath; - } - - public void setFeatureName(String name) { - String nameBits[] = name.split(":"); - String resDom = nameBits.length > 0?nameBits[0]:"orespawn"; - String resPath = nameBits.length > 0?nameBits[1]:name; - this.resLoc = new ResourceLocation(resDom, resPath); - } - - @Override - public ResourceLocation getRegistryName() { - return this.resLoc; - } - - @Override - public Class getRegistryType() { - return IFeatureEntry.class; - } - - @Override - public IFeatureEntry setRegistryName(ResourceLocation arg0) { - this.resLoc = arg0; - return this; - } - - @Override - @SuppressWarnings("unchecked") - public void resolve() { - try { - this.featureClass = (Class)Class.forName(this.featureClassPath); - } catch(ClassNotFoundException e) { - OreSpawn.LOGGER.error(e.getMessage()); - this.featureClass = null; - } - } - } - - public void doDataResolution() { - featuresRegistry.getValues().stream().forEach(ent -> ent.resolve()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java deleted file mode 100644 index 06add54..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/IFeatureEntry.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import net.minecraftforge.registries.IForgeRegistryEntry; - -import com.mcmoddev.orespawn.api.OS4Feature; - -public interface IFeatureEntry extends IForgeRegistryEntry { - Class getFeature(); - String getClassName(); - void setFeatureName(String name); - void resolve(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java deleted file mode 100644 index 6b2f5fb..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/IPresetEntry.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import com.google.gson.JsonElement; - -import net.minecraftforge.registries.IForgeRegistryEntry; - -public interface IPresetEntry extends IForgeRegistryEntry { - JsonElement getData(); - IPresetEntry setData(JsonElement data); -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java b/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java deleted file mode 100644 index e77d545..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/IReplacementEntry.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import java.util.List; - -import com.mcmoddev.orespawn.data.OS4BlockData; -import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraftforge.registries.IForgeRegistryEntry; - -public interface IReplacementEntry extends IForgeRegistryEntry { - List getReplacementData(); - List getReplacementState(); - List getReplacementBlock(); - void setData(List data); - void resolveBlocks(); - OS4BlockStateMatcher getBlockMatcher(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java deleted file mode 100644 index 28b7492..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/PresetsRegistry.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.registries.IForgeRegistryModifiable; -import net.minecraftforge.registries.RegistryBuilder; - -public class PresetsRegistry { - private static final IForgeRegistryModifiable presetsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() - .setName(new ResourceLocation("orespawn", "presets_registry")) - .allowModification() - .setType(IPresetEntry.class) - .setMaxID(Integer.MAX_VALUE) - .create(); - - public static final PresetsRegistry INSTANCE = new PresetsRegistry(); - - PresetsRegistry() { - } - - public JsonElement getPreset(String name) { - return (name.contains(":")?presetsRegistry.getValue(new ResourceLocation(name)):presetsRegistry.getValue(new ResourceLocation("orespawn", name))).getData(); - } - - public void addPreset(String name, JsonElement data) { - this.addPreset(new ResourceLocation("orespawn", name), data); - } - - public void addPreset(ResourceLocation rl, JsonElement data) { - this.addPreset(rl, new DefaultEntry(data)); - } - - public void addPreset(ResourceLocation rl, IPresetEntry entry) { - presetsRegistry.register(entry.setRegistryName(rl)); - } - - private class DefaultEntry implements IPresetEntry { - private ResourceLocation name; - private JsonElement presetData; - - DefaultEntry(final JsonElement data) { - this.presetData = data; - } - - @Override - public ResourceLocation getRegistryName() { - return name; - } - - @Override - public Class getRegistryType() { - return IPresetEntry.class; - } - - @Override - public IPresetEntry setRegistryName(ResourceLocation arg0) { - this.name = arg0; - return this; - } - - @Override - public JsonElement getData() { - return presetData==null?new JsonArray():this.presetData; - } - - @Override - public IPresetEntry setData(JsonElement data) { - this.presetData = data; - return this; - } - - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java b/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java deleted file mode 100644 index f6185a5..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/ReplacementsRegistry.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.DynamicRegistries; -import net.minecraftforge.registries.IForgeRegistryModifiable; -import net.minecraftforge.registries.RegistryBuilder; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.OS4BlockData; -import com.mcmoddev.orespawn.utils.Helpers; -import com.mcmoddev.orespawn.utils.OS4BlockStateMatcher; - -public class ReplacementsRegistry { - private static final IForgeRegistryModifiable replacementsRegistry = (IForgeRegistryModifiable) new RegistryBuilder() - .setName(new ResourceLocation("orespawn", "replacements_registry")) - .allowModification() - .setType(IReplacementEntry.class) - .setMaxID(Integer.MAX_VALUE) - .create(); - - public static final ReplacementsRegistry INSTANCE = new ReplacementsRegistry(); - - ReplacementsRegistry() { - - } - - public void addReplacement(final String repName, final List data) { - if(repName.contains(":")) addReplacement(new ResourceLocation(repName), data); - else addReplacement(new ResourceLocation("orespawn", repName), data); - } - - public void addReplacement(final ResourceLocation loc, final List data) { - DefaultReplacementEntry ent = new DefaultReplacementEntry(loc, data); - addReplacement(ent); - } - - public void addReplacement(final IReplacementEntry entry) { - replacementsRegistry.register(entry); - } - - public IReplacementEntry get(final String name) { - ResourceLocation loc = name.contains(":")?new ResourceLocation(name):new ResourceLocation("orespawn", name); - if (replacementsRegistry.containsKey(loc)) return replacementsRegistry.getValue(loc); - else return replacementsRegistry.getValue(new ResourceLocation("orespawn", "default")); - } - - public void dump() { - replacementsRegistry.getValues() - .forEach(entry -> { - OreSpawn.LOGGER.info("Replacement Entry {} -- contains:", entry.getRegistryName()); - entry.getReplacementState() - .forEach(bs -> OreSpawn.LOGGER.info(">> {}", bs.toString())); - }); - } - - private class DefaultReplacementEntry implements IReplacementEntry { - private ResourceLocation name; - private final List data; - private final List blockStates; - - DefaultReplacementEntry(ResourceLocation loc, List blocks) { - this.name = loc; - this.data = blocks; - this.blockStates = new LinkedList<>(); - } - - @Override - public ResourceLocation getRegistryName() { - return this.name; - } - - @Override - public Class getRegistryType() { - return IReplacementEntry.class; - } - - @Override - public IReplacementEntry setRegistryName(ResourceLocation arg0) { - this.name = arg0; - return this; - } - - @Override - public List getReplacementData() { - return ImmutableList.copyOf(this.data); - } - - @Override - public List getReplacementState() { - return ImmutableList.copyOf(this.blockStates); - } - - @Override - public List getReplacementBlock() { - return ImmutableList.copyOf(this.blockStates.stream() - .map(bls -> bls.getBlockState().getBlock()).collect(Collectors.toList())); - } - - @Override - public void setData(List data) { - Collections.copy(this.data, data); - } - - @Override - public void resolveBlocks() { - data.stream() - .filter(Objects::nonNull) - .map(blockData -> Helpers.deserializeState(String.format("%s[%s]", - blockData.getBlockName(), blockData.getBlockState()))) - .filter(Objects::nonNull) - .forEach(blockStates::add); - } - - @Override - public OS4BlockStateMatcher getBlockMatcher() { - if (this.blockStates.isEmpty()) return null; - - return new OS4BlockStateMatcher(this.blockStates); - } - } - - public void doDataResolution(@SuppressWarnings("unused") DynamicRegistries dynamicRegistries) { - replacementsRegistry.getValues() - .forEach(IReplacementEntry::resolveBlocks); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java deleted file mode 100644 index 45b2fa4..0000000 --- a/src/main/java/com/mcmoddev/orespawn/registries/SpawnStore.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mcmoddev.orespawn.registries; - -import java.util.Map; -import java.util.TreeMap; - -import com.mcmoddev.orespawn.data.SpawnData; - -import net.minecraft.util.registry.DynamicRegistries; - -public class SpawnStore { - private static final Map spawns = new TreeMap<>(); - - public static void add(SpawnData spawnData) { - spawns.put(spawnData.getName(), spawnData); - } - - public static void doResolveData(DynamicRegistries dynamicRegistries) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index 6a59d57..0d4f8ed 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -1,6 +1,7 @@ package com.mcmoddev.orespawn.utils; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.BlockData; import com.mcmoddev.orespawn.data.Constants; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -10,19 +11,39 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.nio.file.Paths; public class Helpers { - public static boolean objectNotNull(Object obj) { - return obj != null; + public static ResourceLocation makeResourceLocationFlat(final String domain, final String path) { + return new ResourceLocation(domain, path); } - public static ResourceLocation makeResourceLocation(String blockName) { - if(blockName.contains(":")) return new ResourceLocation(blockName); - else return new ResourceLocation("minecraft", blockName); + private static Pair getNameBits(final String defaultDomain, final String toSplit) { + String domain; + String path; + if (toSplit.contains(":")) { + String[] bits = toSplit.split(":"); + domain = bits[0]; + path = bits[1]; + } else { + domain = defaultDomain; + path = toSplit; + } + + return Pair.of(domain, path); + } + public static ResourceLocation makeInternalResourceLocation(final String locIn) { + Pair bits = getNameBits("orespawn", locIn); + return makeResourceLocationFlat(bits.getLeft(), bits.getRight()); + } + + public static ResourceLocation makeBlockResourceLocation(final String blockName) { + Pair bits = getNameBits("minecraft", blockName); + return makeResourceLocationFlat(bits.getLeft(), bits.getRight()); } public static @Nullable BlockState deserializeState(final String fullState) { @@ -54,4 +75,13 @@ public static Path makePath(ResourceLocation rl, String type) { return result; } + public static BlockState getBlockFor(BlockData blockData) { + try { + return new BlockStateParser(new StringReader(blockData.getBlockWithState()), false).parse(false).getState(); + } catch (CommandSyntaxException e) { + OreSpawn.LOGGER.error("Error parsing blockstate for {} -- {}", blockData.getBlockWithState(), e.getMessage()); + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java b/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java deleted file mode 100644 index 808f56e..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/OS4BlockStateMatcher.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import java.util.List; - -import com.google.common.collect.ImmutableList; - -import net.minecraft.block.BlockState; - -public class OS4BlockStateMatcher { - private final List statesToMatch; - - public OS4BlockStateMatcher(List blockStates) { - statesToMatch = ImmutableList.copyOf(blockStates); - } - - public boolean matches(final BlockState state) { - if (statesToMatch.isEmpty()) return false; - - return statesToMatch.stream().anyMatch(bs -> (bs == state) || - (bs.matchesBlock(state.getBlock()) && - bs.getProperties().containsAll(state.getProperties()))); - } -} From 7a0c43d5a0b6b9a3f233ef8c12db1510efb4dc1c Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 14 Jul 2021 21:32:33 -0400 Subject: [PATCH 14/37] Data work just about complete, just a few bits left needed... --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 20 +++++- .../com/mcmoddev/orespawn/api/OS4API.java | 15 +++-- .../mcmoddev/orespawn/data/BiomeMatcher.java | 58 +++++++++++++++++ .../com/mcmoddev/orespawn/data/Constants.java | 2 + .../orespawn/data/DimensionMatcher.java | 62 +++++++++++++++++++ .../mcmoddev/orespawn/data/OS4Replacer.java | 12 ++++ .../orespawn/data/ReplacementsStore.java | 32 ++++++++++ .../mcmoddev/orespawn/data/SpawnStore.java | 45 ++++++++++++++ 8 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index bf031c2..f4399bf 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,6 +1,14 @@ package com.mcmoddev.orespawn; +import net.minecraft.client.resources.ReloadListener; +import net.minecraft.item.Item; +import net.minecraft.profiler.IProfiler; +import net.minecraft.resources.IResourceManager; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.AddReloadListenerEvent; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -15,6 +23,9 @@ import com.mcmoddev.orespawn.api.OS4API; import com.mcmoddev.orespawn.loaders.ResourceLoader; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + @Mod("orespawn") public class OreSpawn { // Directly reference a log4j logger. @@ -32,8 +43,9 @@ public OreSpawn() { // Register the doClientStuff method for modloading // Register ourselves for server and other game events we are interested in + MinecraftForge.EVENT_BUS.addListener(this::itemRegistryEvent); MinecraftForge.EVENT_BUS.addListener(this::doServerStartTasks); - //MinecraftForge.EVENT_BUS.register(this); + MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { @@ -53,7 +65,11 @@ private void enqueueIMC(final InterModEnqueueEvent event) { private void processIMC(final InterModProcessEvent event) { } + private void itemRegistryEvent(final RegistryEvent.Register ev) { + OS4API.resolveBlockData(); + } + private void doServerStartTasks(final FMLServerStartingEvent ev) { - OS4API.resolveData(ev.getServer().getDynamicRegistries()); + OS4API.resolveWorldData(ev.getServer().getDynamicRegistries()); } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index c3f05f2..ca41e27 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -15,7 +15,11 @@ import com.google.gson.Gson; import com.mcmoddev.orespawn.data.*; +import net.minecraft.client.resources.ReloadListener; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.AddReloadListenerEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; import org.apache.commons.io.FileUtils; import com.google.gson.stream.JsonReader; @@ -24,6 +28,9 @@ import net.minecraft.util.registry.DynamicRegistries; import org.apache.commons.lang3.tuple.Pair; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import static com.mcmoddev.orespawn.utils.Helpers.makeInternalResourceLocation; public class OS4API { @@ -121,13 +128,12 @@ public static void addKnownConfig(ResourceLocation config) { } public static void resolveBlockData() { - // TODO: We get called from the proper event to resolve our data - // TODO: This means we get called at the start of RegistryEvent.Register, hopefully... + ReplacementsStore.resolveBlocks(); + SpawnStore.resolveSpawnBlocks(); } public static void resolveWorldData(DynamicRegistries dynamicRegistries) { - // TODO: We get called from the proper event to resolve our data - // TODO: This means we get called at the start of ServerStarting, hopefully... + SpawnStore.resolveBiomesAndDimensions(dynamicRegistries); } public static void loadFeaturesFromStream(InputStream newInputStream) { @@ -158,6 +164,7 @@ public static void loadReplacementsFromStream(InputStream newInputStream) { JsonArray entries = x.getValue().getAsJsonArray(); List blocks = new LinkedList<>(); entries.forEach( entry -> blocks.add(BlockData.makeFromJson(entry.getAsJsonObject()))); + ReplacementsStore.add(entryName, blocks); } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java new file mode 100644 index 0000000..9fe1a96 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java @@ -0,0 +1,58 @@ +package com.mcmoddev.orespawn.data; + +import com.sun.jndi.rmi.registry.RegistryContextFactory; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.util.registry.MutableRegistry; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.mcmoddev.orespawn.utils.Helpers.makeBlockResourceLocation; + +public class BiomeMatcher { + private final List> allowedBiomes = new LinkedList<>(); + public static BiomeMatcher buildFromData(List biomeBlacklist, List biomeWhitelist, DynamicRegistries dynamicRegistries) { + BiomeMatcher result = new BiomeMatcher(); + MutableRegistry registry = dynamicRegistries.getRegistry(Registry.BIOME_KEY); + if (biomeWhitelist.size() == 0 || biomeWhitelist.contains("orespawn4:any")) + result.defaultLoad(registry); + else + result.loadWhitelist(biomeWhitelist); + + if (biomeBlacklist.size() > 0) + result.clearBlacklistedBits(biomeBlacklist); + + return result; + } + + public boolean biomeMatches(final ResourceLocation biomeRL) { + return biomeMatches(RegistryKey.getOrCreateKey(Registry.BIOME_KEY, biomeRL)); + } + + public boolean biomeMatches(RegistryKey biomeKey) { + return allowedBiomes.contains(biomeKey); + } + + public boolean biomeMatches(final String biomeName) { + return biomeMatches(makeBlockResourceLocation(biomeName)); + } + + private void clearBlacklistedBits(List biomeBlacklist) { + biomeBlacklist.stream().map( name -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, makeBlockResourceLocation(name))) + .filter( biomeKey -> allowedBiomes.contains(biomeKey)) + .forEach( biomeKey -> allowedBiomes.remove(biomeKey)); + } + + private void loadWhitelist(List biomeWhitelist) { + allowedBiomes.addAll( biomeWhitelist.stream().map( name -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, makeBlockResourceLocation(name))).collect(Collectors.toList())); + } + + private void defaultLoad(MutableRegistry registry) { + allowedBiomes.addAll( registry.getEntries().stream().map( entry -> entry.getKey()).collect(Collectors.toList()) ); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index cb24c49..c413fc8 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -8,6 +8,8 @@ public class Constants { public static final String CRASH_SECTION = "OreSpawn Version"; public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4, FileBits.SYSCONF); public static final Path JSONPATH = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4); + public static final String MODID = "mmd_orespawn"; + public static class ConfigNames { public static final String REPLACEMENT = "replaces"; diff --git a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java new file mode 100644 index 0000000..9827a73 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java @@ -0,0 +1,62 @@ +package com.mcmoddev.orespawn.data; + +import com.mcmoddev.orespawn.utils.Helpers; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.util.registry.MutableRegistry; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.Dimension; +import net.minecraft.world.DimensionType; +import net.minecraft.world.World; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class DimensionMatcher { + private final List> allowedDimensions = new LinkedList<>(); + public static DimensionMatcher buildFromData(List dimensionList, DynamicRegistries dynamicRegistries) { + DimensionMatcher result = new DimensionMatcher(); + MutableRegistry registry = dynamicRegistries.getRegistry(Registry.DIMENSION_KEY); + DimensionType overWorldType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.OVERWORLD).getDimensionType(); + DimensionType netherType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.THE_NETHER).getDimensionType(); + DimensionType endType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.THE_END).getDimensionType(); + + if (dimensionList.contains("orespawn4:overworlds")) + result.loadMatching(overWorldType, registry); + if (dimensionList.contains("orespawn4:nethers")) + result.loadMatching(netherType, registry); + if (dimensionList.contains("orespawn4:ends")) + result.loadMatching(endType, registry); + + if (dimensionList.size() > 0) + result.loadList(dimensionList); + + return result; + } + + public boolean matches(final String name) { + return matches(Helpers.makeBlockResourceLocation(name)); + } + + public boolean matches(final ResourceLocation resourceLocation) { + return matches(RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, resourceLocation)); + } + + public boolean matches(RegistryKey registryKey) { + return allowedDimensions.contains(registryKey); + } + + private void loadList(List dimensionList) { + allowedDimensions.addAll(dimensionList.stream().filter(name -> !name.startsWith("orespawn4:")) + .map( Helpers::makeBlockResourceLocation ) + .map( resourceLocation -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, resourceLocation) ) + .collect(Collectors.toList())); + } + + private void loadMatching(DimensionType matchType, MutableRegistry registry) { + allowedDimensions.addAll(registry.getEntries().stream().filter(entry -> entry.getValue().getDimensionType().isSame(matchType)).map(Map.Entry::getKey).collect(Collectors.toList())); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java b/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java new file mode 100644 index 0000000..002e4a1 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java @@ -0,0 +1,12 @@ +package com.mcmoddev.orespawn.data; + +import net.minecraft.block.BlockState; + +import java.util.List; + +public class OS4Replacer { + + public OS4Replacer(List blockStates) { + + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java b/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java new file mode 100644 index 0000000..4378154 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java @@ -0,0 +1,32 @@ +package com.mcmoddev.orespawn.data; + +import com.mcmoddev.orespawn.utils.Helpers; +import net.minecraft.block.BlockState; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class ReplacementsStore { + private static final Map> baseBlocks = new HashMap<>(1024); + private static final Map> resolved = new HashMap<>(1024); + + public static void add(final String entryName, final List rawBlocks) { + baseBlocks.put(entryName, rawBlocks); + } + + public static void resolveBlocks() { + baseBlocks.entrySet().stream() + .forEach( entry -> { + String name = entry.getKey(); + List resolvedBlocks = new LinkedList<>(); + entry.getValue().forEach( blockData -> resolvedBlocks.add(Helpers.deserializeState(blockData.getBlockWithState()))); + resolved.put(name, resolvedBlocks); + }); + } + + public static OS4Replacer get(String replacementName) { + return new OS4Replacer(resolved.get(replacementName)); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java index c6aecbc..654541f 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java +++ b/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java @@ -4,6 +4,10 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.utils.Helpers; +import net.minecraft.block.BlockState; +import net.minecraft.util.WeightedList; +import net.minecraft.util.registry.DynamicRegistries; import java.util.*; @@ -53,6 +57,17 @@ public static void loadFromJson(final Map.Entry dataIn) { }); } + public static void resolveSpawnBlocks() { + spawns.entrySet().forEach(entry -> { + entry.getValue().resolveBlocks(); + entry.getValue().resolveReplacer(); + }); + } + + public static void resolveBiomesAndDimensions(DynamicRegistries dynamicRegistries) { + spawns.entrySet().forEach(entry -> entry.getValue().resolveOtherData(dynamicRegistries)); + } + public static class SpawnData { private String featureName; private String replacementName; @@ -63,11 +78,22 @@ public static class SpawnData { private String spawnName; private List blocks; + private WeightedList blockStates; + private OS4Replacer replacer; + private BiomeMatcher biomes; + private DimensionMatcher dimensions; + + private boolean resolvedBlocks = false; + private boolean resolvedReplacements = false; + private boolean resolvedOther = false; + + public SpawnData() { dimensionList = new LinkedList<>(); biomeWhitelist = new LinkedList<>(); biomeBlacklist = new LinkedList<>(); blocks = new LinkedList<>(); + blockStates = new WeightedList<>(); } public SpawnData setFeatureName(final String name) { @@ -133,5 +159,24 @@ public SpawnData addBlock(final BlockData block) { blocks.add(block); return this; } + + public void resolveReplacer() { + if (resolvedReplacements) return; + replacer = ReplacementsStore.get(replacementName); + resolvedReplacements = true; + } + + public void resolveBlocks() { + blocks.stream().forEach( blockData -> { + BlockState baseState = Helpers.deserializeState(blockData.getBlockWithState()); + blockStates.addWeighted(baseState, blockData.getChance()); + }); + resolvedBlocks = true; + } + + public void resolveOtherData(DynamicRegistries dynamicRegistries) { + biomes = BiomeMatcher.buildFromData(biomeBlacklist, biomeWhitelist, dynamicRegistries); + dimensions = DimensionMatcher.buildFromData(dimensionList, dynamicRegistries); + } } } From d869298b937cf1a7e10f3ae2502a51866fdcc459 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 15 Jul 2021 00:07:01 -0400 Subject: [PATCH 15/37] Data loading and resolution complete. TODO, in no particular order: 1) implement new world-gen wrapper setup 2) register a world-gen wrapper per spawn 3) implement features from OS3 4) implement feature that wraps vanilla's generation bit 5) add CrT integration stuff --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 1 + .../com/mcmoddev/orespawn/api/OS4API.java | 4 ++ .../com/mcmoddev/orespawn/api/OS4Feature.java | 11 ++++ .../mcmoddev/orespawn/data/FeaturesStore.java | 24 +++++++- .../orespawn/utils/ReflectionHelper.java | 60 +++++++++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index f4399bf..4dd7071 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -60,6 +60,7 @@ private void doClientStuff(final FMLClientSetupEvent event) { } private void enqueueIMC(final InterModEnqueueEvent event) { + OS4API.resolveFeatures(); } private void processIMC(final InterModProcessEvent event) { diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index ca41e27..d3ca1ff 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -203,4 +203,8 @@ public static void loadSpawnsFromStream(InputStream newInputStream) { rawSpawnData.entrySet().forEach( entry -> SpawnStore.loadFromJson(entry)); } + + public static void resolveFeatures() { + FeaturesStore.resolveFeatures(); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java new file mode 100644 index 0000000..062d7ec --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java @@ -0,0 +1,11 @@ +package com.mcmoddev.orespawn.api; + +import com.google.gson.JsonObject; +import com.mcmoddev.orespawn.data.SpawnStore; + +public interface OS4Feature { + String setName(String spawnName); + String getDefaultParameters(); + String setParameters(JsonObject parametersBlock); + String setData(SpawnStore.SpawnData spawnData); +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java b/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java index c425d5e..68113ae 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java +++ b/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java @@ -1,15 +1,37 @@ package com.mcmoddev.orespawn.data; import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS4Feature; +import com.mcmoddev.orespawn.utils.ReflectionHelper; +import org.apache.commons.lang3.tuple.Pair; import java.util.HashMap; import java.util.Map; +import java.util.Objects; public class FeaturesStore { - private static Map featuresList = new HashMap<>(1024); + private static final Map featuresList = new HashMap<>(1024); + private static final Map> features = new HashMap<>(1024); public static void addNewFeature(String key, String value) { if (!featuresList.containsKey(key)) featuresList.put(key, value); else OreSpawn.LOGGER.error("Duplicate feature name {} with value {} found during load, ignoring.", key, value); } + + public static void resolveFeatures() { + featuresList.entrySet().stream() + .map( entry -> { + String name = entry.getKey(); + String FQCN = entry.getValue(); + Class resolved = ReflectionHelper.getFeatureNamed(name, FQCN); + if (Objects.nonNull(resolved)) return Pair.of(name, resolved); + else return null; + }) + .filter(Objects::nonNull) + .forEach(p -> features.put(p.getKey(), p.getValue())); + if (features.size() == 0) { + OreSpawn.LOGGER.fatal("No Features Available! Hard Exit!"); + throw new NullPointerException(); + } + } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java b/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java new file mode 100644 index 0000000..92ccbcd --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java @@ -0,0 +1,60 @@ +package com.mcmoddev.orespawn.utils; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.api.OS4Feature; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class ReflectionHelper { + public static Class getFeatureNamed(final String name, final String fqcn) { + Class resultBase; + Class result = null; + + try { + resultBase = Class.forName(fqcn); + if (resultBase.isAssignableFrom(OS4Feature.class)) result = (Class) resultBase; + } catch (ClassNotFoundException e) { + OreSpawn.LOGGER.error("Error in getting reference to class {} for feature {} -- {}", fqcn, name, e.getMessage()); + e.printStackTrace(); + return null; + } + + return result; + } + + public static Constructor getConstructorForFeature(Class clazz) { + Constructor constructor = null; + try { + constructor = clazz.getConstructor(); + } catch (NoSuchMethodException e) { + OreSpawn.LOGGER.error("Unable to get constructor for {} - {}", clazz.getCanonicalName(), e.getMessage()); + e.printStackTrace(); + return null; + } + + return constructor; + } + + public static OS4Feature getFeatureClassInstance(Constructor featureClassConstructor) { + try { + return featureClassConstructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + OreSpawn.LOGGER.error("Could not instantiate an instance of {} -- {}", featureClassConstructor.getClass().getCanonicalName(), e.getMessage()); + e.printStackTrace(); + return null; + } + } + + public static OS4Feature getFeatureFromName(final String featureClassName, final String featureName) { + Class baseClass = getFeatureNamed(featureName, featureClassName); + Constructor classConstructor; + OS4Feature instance; + if (baseClass != null) classConstructor = getConstructorForFeature(baseClass); + else return null; + if (classConstructor != null) instance = getFeatureClassInstance(classConstructor); + else return null; + + return instance; + } +} From 2edae3b77b2b49d0b4e59822a28fabdf25fc892d Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 15 Jul 2021 22:43:22 -0400 Subject: [PATCH 16/37] Stumbling steps forward... It appears that Mojang has inextricably tied world generation features to datapacks and the JSON they contain. Current OreSpawn configuration system is going away. OpenLoader will be required for any user configs, otherwise mods will just have to include their own data-packs to cover spawns. --- TODO.txt | 5 +++ .../orespawn/world/WorldGenHandler.java | 29 +++++++++++++++ .../world/gen/DefaultFeatureConfig.java | 35 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 TODO.txt create mode 100644 src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java create mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..b945ff3 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,5 @@ +1) Toss entire JSON loading and data management system +2) Implement BiomeMatcher, OS4BlockMatcher and DimensionMatcher with codecs +3) Each Feature gets a Codec and Configuration unique to it +4) Register IFeature and IFeatureConfiguration stuff +5) Fully implement all available features diff --git a/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java b/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java new file mode 100644 index 0000000..6f12315 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java @@ -0,0 +1,29 @@ +package com.mcmoddev.orespawn.world; + +import com.mcmoddev.orespawn.data.Constants; +import com.mcmoddev.orespawn.utils.Helpers; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.WorldGenRegistries; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraftforge.event.world.BiomeLoadingEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; + +@EventBusSubscriber(modid= Constants.MODID) +public class WorldGenHandler { + +/* + @SubscribeEvent + public static void onBiomesLoaded(BiomeLoadingEvent ev) { + } +*/ + public static void doFeatureRegistration() { + + } + + private static ConfiguredFeature register(String key, ConfiguredFeature configuredFeature) { + return Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, Helpers.makeInternalResourceLocation(key), configuredFeature); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java new file mode 100644 index 0000000..3ebc3e0 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java @@ -0,0 +1,35 @@ +package com.mcmoddev.orespawn.world.gen; + +import com.electronwill.nightconfig.core.conversion.InvalidValueException; +import com.mcmoddev.orespawn.data.BiomeMatcher; +import com.mcmoddev.orespawn.data.DimensionMatcher; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BlockState; +import net.minecraft.block.pattern.BlockMatcher; +import net.minecraft.util.WeightedList; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.IFeatureConfig; + +import java.util.stream.Stream; + +public class DefaultFeatureConfig implements IFeatureConfig { + public final BlockMatcher matcher; + public final int size; + public final WeightedList blocks; + public final int minHeight; + public final int maxHeight; + public final BiomeMatcher biomeMatch; + public final DimensionMatcher dimensionMatch; + + public DefaultFeatureConfig(BlockMatcher repl, WeightedList blocks, int sz, int minY, int maxY, BlockMatcher blockMatcher, BiomeMatcher biomeMatcher, DimensionMatcher dimensionMatcher) { + this.matcher = repl; + this.size = sz; + this.blocks = blocks; + this.minHeight = minY; + this.maxHeight = maxY; + this.biomeMatch = biomeMatcher; + this.dimensionMatch = dimensionMatcher; + } +} From e316bf5b3399a7849f18ca2610c2ffdd6bc024f7 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 15 Jul 2021 23:18:52 -0400 Subject: [PATCH 17/37] Delete most of the latest work See TODO.txt for a proper run-down of what is coming. Basic overview: Mojang's push towards having as much as possible configurable via datapacks has pushed me to need to change things so that OreSpawns features are exposed in the same manner and configured in a similar manner. --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 20 +- .../com/mcmoddev/orespawn/api/OS4API.java | 207 +---------------- .../com/mcmoddev/orespawn/api/OS4Feature.java | 11 - .../com/mcmoddev/orespawn/data/BlockData.java | 63 ------ .../mcmoddev/orespawn/data/FeaturesStore.java | 37 --- .../mcmoddev/orespawn/data/OS4Replacer.java | 12 - .../mcmoddev/orespawn/data/PresetsStore.java | 73 ------ .../orespawn/data/ReplacementsStore.java | 32 --- .../mcmoddev/orespawn/data/SpawnStore.java | 182 --------------- .../orespawn/loaders/ResourceLoader.java | 210 ------------------ .../com/mcmoddev/orespawn/utils/Helpers.java | 11 - 11 files changed, 3 insertions(+), 855 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/BlockData.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 4dd7071..966b2d5 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,14 +1,8 @@ package com.mcmoddev.orespawn; -import net.minecraft.client.resources.ReloadListener; import net.minecraft.item.Item; -import net.minecraft.profiler.IProfiler; -import net.minecraft.resources.IResourceManager; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.AddReloadListenerEvent; import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -20,12 +14,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.mcmoddev.orespawn.api.OS4API; -import com.mcmoddev.orespawn.loaders.ResourceLoader; - -import javax.annotation.Nonnull; -import javax.annotation.ParametersAreNonnullByDefault; - @Mod("orespawn") public class OreSpawn { // Directly reference a log4j logger. @@ -49,10 +37,6 @@ public OreSpawn() { } private void setup(final FMLCommonSetupEvent event) { - OS4API.loadKnownConfigs(); - ResourceLoader loader = new ResourceLoader(); - loader.runLoaders(); - OS4API.saveKnownConfigs(); } @@ -60,17 +44,15 @@ private void doClientStuff(final FMLClientSetupEvent event) { } private void enqueueIMC(final InterModEnqueueEvent event) { - OS4API.resolveFeatures(); } private void processIMC(final InterModProcessEvent event) { } private void itemRegistryEvent(final RegistryEvent.Register ev) { - OS4API.resolveBlockData(); + } private void doServerStartTasks(final FMLServerStartingEvent ev) { - OS4API.resolveWorldData(ev.getServer().getDynamicRegistries()); } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java index d3ca1ff..0026ca8 100644 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java @@ -1,210 +1,7 @@ package com.mcmoddev.orespawn.api; -import java.io.*; -import java.nio.charset.Charset; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.google.gson.JsonParser; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.Gson; - -import com.mcmoddev.orespawn.data.*; -import net.minecraft.client.resources.ReloadListener; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.event.AddReloadListenerEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import org.apache.commons.io.FileUtils; - -import com.google.gson.stream.JsonReader; -import com.mcmoddev.orespawn.OreSpawn; - -import net.minecraft.util.registry.DynamicRegistries; -import org.apache.commons.lang3.tuple.Pair; - -import javax.annotation.Nonnull; -import javax.annotation.ParametersAreNonnullByDefault; - -import static com.mcmoddev.orespawn.utils.Helpers.makeInternalResourceLocation; - public class OS4API { - private static final ConfigBlacklist configBlacklist = new ConfigBlacklist(); - private static final List knownConfigs = new LinkedList<>(); - - /* - * Load the `Active Configs` file - * This one needs a bit more thought, right now its just a list of filenames... - * It might be better to have some structure so mods themselves can be listed - */ - public static void loadIntegrationWhitelist() { - Path p = Constants.SYSCONF.resolve(Constants.FileBits.ALLOWED_MODS); - final JsonParser parser = new JsonParser(); - String rawJson; - - try { - rawJson = FileUtils.readFileToString(p.toFile(), Charset.defaultCharset()); - } catch (final IOException e) { - OreSpawn.LOGGER.error(e.getMessage()); - return; - } - - JsonObject topLevel = parser.parse(rawJson).getAsJsonObject(); - if (topLevel.has("mods")) - for (JsonElement el : topLevel.get("mods").getAsJsonArray()) - configBlacklist.addMod(el.getAsString()); - if (topLevel.has("configs")) - for (JsonElement el : topLevel.get("configs").getAsJsonArray()) - configBlacklist.addConfig(el.getAsString()); - } - - private static JsonElement getParsedJson(InputStream stream) { - InputStreamReader readerForStream = new InputStreamReader(stream); - JsonReader jsonReader = new JsonReader(readerForStream); - JsonParser parserToReturnFrom = new JsonParser(); - return parserToReturnFrom.parse(jsonReader); - } - - public static void loadPresetsFromStream(InputStream inFile) { - JsonObject elements = getParsedJson(inFile).getAsJsonObject(); - - // let the storage actually do the load - it can do the recursion necessary to get the name and all that - PresetsStore.load(elements); - } - - public static boolean isAllowed(final String checkName) { - return configBlacklist.isAllowed(checkName); - } - - public static void loadKnownConfigs() { - Path knownConfigs = Paths.get(Constants.SYSCONF.toAbsolutePath().toString(), "known-configs.json"); - if (knownConfigs.toFile().exists() && knownConfigs.toFile().canRead()) { - JsonParser parser = new JsonParser(); - JsonElement root; - try ( - FileInputStream baseInput = new FileInputStream(knownConfigs.toFile()); - BufferedInputStream dataInput = new BufferedInputStream(baseInput); - InputStreamReader theReader = new InputStreamReader(dataInput)) { - root = parser.parse(new JsonReader(theReader)); - } catch(IOException ex) { - OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); - ex.printStackTrace(); - return; - } - - root.getAsJsonArray().forEach( el -> addModConfig(el.toString())); - } - } - - private static void addModConfig(final String itemIn) { - addKnownConfig(makeInternalResourceLocation(itemIn)); - } - - public static void saveKnownConfigs() { - Path knownConfigs = Paths.get(Constants.SYSCONF.toAbsolutePath().toString(), "known-configs.json"); - if (knownConfigs.toFile().canWrite()) { - try (FileOutputStream outStream = new FileOutputStream(knownConfigs.toString()); - OutputStreamWriter writer = new OutputStreamWriter(outStream)) { - Gson gson = new Gson(); - writer.write(gson.toJson(knownConfigs)); - } catch (IOException ex) { - OreSpawn.LOGGER.error("Error saving known configs file: {}", ex.getMessage()); - ex.printStackTrace(); - } - } - } - - public static boolean isKnownConfig(ResourceLocation config) { - return knownConfigs.contains(config); - } - - public static void addKnownConfig(ResourceLocation config) { - knownConfigs.add(config); - } - - public static void resolveBlockData() { - ReplacementsStore.resolveBlocks(); - SpawnStore.resolveSpawnBlocks(); - } - - public static void resolveWorldData(DynamicRegistries dynamicRegistries) { - SpawnStore.resolveBiomesAndDimensions(dynamicRegistries); - } - - public static void loadFeaturesFromStream(InputStream newInputStream) { - /* - { - "feature name" : "feature classpath", - ... - } - */ - JsonObject featuresIn = getParsedJson(newInputStream).getAsJsonObject(); - - featuresIn.entrySet().stream() - .map( baseEntry -> Pair.of(baseEntry.getKey(), baseEntry.getValue().getAsString()) ) - .forEach( manipulated -> FeaturesStore.addNewFeature( manipulated.getKey(), manipulated.getValue())); - } - - public static void loadReplacementsFromStream(InputStream newInputStream) { - /* - { - "replacement name" : [ { block data }, ... ], - ... - } - */ - JsonObject replacementsIn = getParsedJson(newInputStream).getAsJsonObject(); - - for ( Map.Entry x : replacementsIn.entrySet()) { - String entryName = x.getKey(); - JsonArray entries = x.getValue().getAsJsonArray(); - List blocks = new LinkedList<>(); - entries.forEach( entry -> blocks.add(BlockData.makeFromJson(entry.getAsJsonObject()))); - ReplacementsStore.add(entryName, blocks); - } - } - - public static void loadSpawnsFromStream(InputStream newInputStream) { - /* - { - "version": 2.0, - "spawns": { - "spawn name": { - "feature": "feature name or $.preset", - "replacements": "replacement name or $.preset or array", - "dimensions": array - whitelist, with special values or $.preset, - "biomes": object of whitelist/blacklist segments or $.preset, - "blocks": array of block data or $.preset, - "parameters": variable object or $.preset - }, - ... - } - } - */ - JsonObject spawnFileData = getParsedJson(newInputStream).getAsJsonObject(); - - if (!spawnFileData.has("version") || - (spawnFileData.has("version") && spawnFileData.get("version").getAsFloat() != 2.0)) { - OreSpawn.LOGGER.error("Spawn File does not have a version or is of an unhandled or unknown version. Ignoring."); - return; - } - if (!spawnFileData.has("spawns")) { - OreSpawn.LOGGER.error("Spawn File of incorrect format or has no spawns, ignoring."); - return; - } - - OreSpawn.LOGGER.info("Loading Spawns, version {}", spawnFileData.get("version").getAsFloat()); - - JsonObject rawSpawnData = spawnFileData.get("spawns").getAsJsonObject(); - - rawSpawnData.entrySet().forEach( entry -> SpawnStore.loadFromJson(entry)); - } - - public static void resolveFeatures() { - FeaturesStore.resolveFeatures(); + private OS4API() { + // purposefully blank and private } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java b/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java deleted file mode 100644 index 062d7ec..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4Feature.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mcmoddev.orespawn.api; - -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.data.SpawnStore; - -public interface OS4Feature { - String setName(String spawnName); - String getDefaultParameters(); - String setParameters(JsonObject parametersBlock); - String setData(SpawnStore.SpawnData spawnData); -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/BlockData.java b/src/main/java/com/mcmoddev/orespawn/data/BlockData.java deleted file mode 100644 index 6c1d916..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/BlockData.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.gson.JsonObject; - -public class BlockData { - private final String blockName; - private final String blockState; - private final boolean hasState; - private final int chanceToAppear; - - public BlockData(final String name, final String state, final int chance) { - blockName = name; - blockState = state; - hasState = true; - chanceToAppear = chance; - } - - public BlockData(final String name, final String state) { - blockName = name; - blockState = state; - hasState = true; - chanceToAppear = -1; - } - - public BlockData(final String name, final int chance) { - blockName = name; - chanceToAppear = chance; - hasState = false; - blockState = ""; - } - - public BlockData(final String name) { - blockName = name; - chanceToAppear = -1; - hasState = false; - blockState = ""; - } - - public String getBlockWithState() { - return hasState?String.format("%s[%s]", blockName, blockState):blockName; - } - - public int getChance() { - return chanceToAppear; - } - - public static BlockData makeFromJson(JsonObject dataIn) { - if (dataIn.has("state")) { - if (dataIn.has("chance")) { - return new BlockData(dataIn.get("name").getAsString(), - dataIn.get("state").getAsString(), dataIn.get("chance").getAsInt()); - } else { - return new BlockData(dataIn.get("name").getAsString(), dataIn.get("state").getAsString()); - } - } else { - if (dataIn.has("chance")) { - return new BlockData(dataIn.get("name").getAsString(), dataIn.get("chance").getAsInt()); - } else { - return new BlockData(dataIn.get("name").getAsString()); - } - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java b/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java deleted file mode 100644 index 68113ae..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/FeaturesStore.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4Feature; -import com.mcmoddev.orespawn.utils.ReflectionHelper; -import org.apache.commons.lang3.tuple.Pair; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class FeaturesStore { - private static final Map featuresList = new HashMap<>(1024); - private static final Map> features = new HashMap<>(1024); - - public static void addNewFeature(String key, String value) { - if (!featuresList.containsKey(key)) featuresList.put(key, value); - else OreSpawn.LOGGER.error("Duplicate feature name {} with value {} found during load, ignoring.", key, value); - } - - public static void resolveFeatures() { - featuresList.entrySet().stream() - .map( entry -> { - String name = entry.getKey(); - String FQCN = entry.getValue(); - Class resolved = ReflectionHelper.getFeatureNamed(name, FQCN); - if (Objects.nonNull(resolved)) return Pair.of(name, resolved); - else return null; - }) - .filter(Objects::nonNull) - .forEach(p -> features.put(p.getKey(), p.getValue())); - if (features.size() == 0) { - OreSpawn.LOGGER.fatal("No Features Available! Hard Exit!"); - throw new NullPointerException(); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java b/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java deleted file mode 100644 index 002e4a1..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/OS4Replacer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import net.minecraft.block.BlockState; - -import java.util.List; - -public class OS4Replacer { - - public OS4Replacer(List blockStates) { - - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java b/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java deleted file mode 100644 index 515980b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/PresetsStore.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import java.util.HashMap; -import java.util.Map; - -public class PresetsStore { - private static final Map presets = new HashMap<>(1024); - - /* - Presets File Format: - { - "section name": { - "item name": value_as_needed_for_section - }, - ... - } - - Sections: feature, replacement, parameter, dimension, biome - */ - - public static void load(final JsonObject presetsIn) { - presetsIn.entrySet().forEach(entry -> { - entry.getValue().getAsJsonObject().entrySet().forEach( - presetEntry -> { - final String fullName = String.format("$.%s.%s", entry.getKey(), presetEntry.getKey()); - presets.put(fullName, loadPresetDispatch(entry.getKey(), presetEntry.getValue())); - }); - }); - } - - private static JsonElement loadPresetDispatch(final String sectionName, final JsonElement value) { - // TODO: sectionName should be used to properly handle the section loading - return value; - } - - public static JsonElement getPreset(final String presetName) { - String lookupName = presetName.startsWith("$.")?presetName:String.format("$.%s", presetName); - if (presets.containsKey(lookupName)) return presets.get(lookupName); - return new JsonArray(); - } - - public static String interpolateString(JsonElement value) { - if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { - String getMe = value.getAsString(); - return presets.containsKey(getMe)?getPreset(getMe).getAsString():getMe; - } - - return value.getAsString(); - } - - public static JsonArray interpolateArray(JsonElement value) { - if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { - String getMe = value.getAsString(); - return getPreset(getMe).getAsJsonArray(); - } - - return value.getAsJsonArray(); - } - - public static JsonObject interpolateObject(JsonElement value) { - if (value.isJsonPrimitive() && value.getAsString().startsWith("$.")) { - String getMe = value.getAsString(); - JsonElement preset = getPreset(getMe); - return preset.isJsonObject()?preset.getAsJsonObject():new JsonObject(); - } - - return value.getAsJsonObject(); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java b/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java deleted file mode 100644 index 4378154..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/ReplacementsStore.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.utils.Helpers; -import net.minecraft.block.BlockState; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class ReplacementsStore { - private static final Map> baseBlocks = new HashMap<>(1024); - private static final Map> resolved = new HashMap<>(1024); - - public static void add(final String entryName, final List rawBlocks) { - baseBlocks.put(entryName, rawBlocks); - } - - public static void resolveBlocks() { - baseBlocks.entrySet().stream() - .forEach( entry -> { - String name = entry.getKey(); - List resolvedBlocks = new LinkedList<>(); - entry.getValue().forEach( blockData -> resolvedBlocks.add(Helpers.deserializeState(blockData.getBlockWithState()))); - resolved.put(name, resolvedBlocks); - }); - } - - public static OS4Replacer get(String replacementName) { - return new OS4Replacer(resolved.get(replacementName)); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java b/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java deleted file mode 100644 index 654541f..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/SpawnStore.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.utils.Helpers; -import net.minecraft.block.BlockState; -import net.minecraft.util.WeightedList; -import net.minecraft.util.registry.DynamicRegistries; - -import java.util.*; - -public class SpawnStore { - private static final Map spawns = new TreeMap<>(); - - public static void loadFromJson(final Map.Entry dataIn) { - SpawnData newSpawn = new SpawnData(); - dataIn.getValue().getAsJsonObject().entrySet().forEach(baseEntry -> { - newSpawn.setName(baseEntry.getKey()); - baseEntry.getValue().getAsJsonObject().entrySet().forEach(entry -> { - switch (entry.getKey()) { - case "feature": - newSpawn.setFeatureName(PresetsStore.interpolateString(entry.getValue())); - break; - case "replaces": - newSpawn.setReplacementName(PresetsStore.interpolateString(entry.getValue())); - break; - case "dimensions": - JsonArray midpoint = PresetsStore.interpolateArray(entry.getValue()); - for (JsonElement jsonElement : midpoint) { - String dimName = jsonElement.getAsString(); - newSpawn.addDimension(dimName); - } - break; - case "biomes": - JsonObject baseData = PresetsStore.interpolateObject(entry.getValue()); - for (Map.Entry ent : baseData.entrySet()) { - if (ent.getKey().matches("whitelist")) { - ent.getValue().getAsJsonArray().forEach(item -> newSpawn.addBiomeToWhitelist(item.getAsString())); - } else if (ent.getKey().matches("blacklist")) { - ent.getValue().getAsJsonArray().forEach(item -> newSpawn.addBiomeToBlacklist(item.getAsString())); - } - } - break; - case "parameters": - newSpawn.setParameters(entry.getValue().getAsJsonObject()); - break; - case "blocks": - JsonArray blockData = PresetsStore.interpolateArray(entry.getValue()); - blockData.forEach( block -> newSpawn.addBlock(BlockData.makeFromJson(block.getAsJsonObject()))); - break; - default: - OreSpawn.LOGGER.error("Unkown entry {} in spawn {}", entry.getKey(), baseEntry.getKey()); - } - }); - }); - } - - public static void resolveSpawnBlocks() { - spawns.entrySet().forEach(entry -> { - entry.getValue().resolveBlocks(); - entry.getValue().resolveReplacer(); - }); - } - - public static void resolveBiomesAndDimensions(DynamicRegistries dynamicRegistries) { - spawns.entrySet().forEach(entry -> entry.getValue().resolveOtherData(dynamicRegistries)); - } - - public static class SpawnData { - private String featureName; - private String replacementName; - private List dimensionList; - private List biomeWhitelist; - private List biomeBlacklist; - private JsonObject parameters; - private String spawnName; - private List blocks; - - private WeightedList blockStates; - private OS4Replacer replacer; - private BiomeMatcher biomes; - private DimensionMatcher dimensions; - - private boolean resolvedBlocks = false; - private boolean resolvedReplacements = false; - private boolean resolvedOther = false; - - - public SpawnData() { - dimensionList = new LinkedList<>(); - biomeWhitelist = new LinkedList<>(); - biomeBlacklist = new LinkedList<>(); - blocks = new LinkedList<>(); - blockStates = new WeightedList<>(); - } - - public SpawnData setFeatureName(final String name) { - featureName = name; - return this; - } - - public SpawnData setReplacementName(final String name) { - replacementName = name; - return this; - } - - public SpawnData addDimension(final String dimensionName) { - dimensionList.add(dimensionName); - return this; - } - - public SpawnData setBiomesAll() { - biomeWhitelist = Collections.emptyList(); - biomeBlacklist = Arrays.asList("orespawn4:any"); - return this; - } - - public SpawnData addBiomeToWhitelist(final String biomeName) { - biomeWhitelist.add(biomeName); - return this; - } - - public SpawnData addBiomeToBlacklist(final String biomeName) { - biomeBlacklist.add(biomeName); - return this; - } - - public SpawnData setAllOverworld() { - dimensionList = Arrays.asList("orespawn4:overworlds"); - return this; - } - - public SpawnData setAllNether() { - dimensionList = Arrays.asList("orespawn4:nethers"); - - return this; - } - - public SpawnData setAllVoidOrEnd() { - dimensionList = Arrays.asList("orespawn4:ends"); - - return this; - } - - public SpawnData setParameters(final JsonObject parametersElement) { - parameters = parametersElement; - - return this; - } - - public SpawnData setName(final String name) { - spawnName = name; - return this; - } - - public SpawnData addBlock(final BlockData block) { - blocks.add(block); - return this; - } - - public void resolveReplacer() { - if (resolvedReplacements) return; - replacer = ReplacementsStore.get(replacementName); - resolvedReplacements = true; - } - - public void resolveBlocks() { - blocks.stream().forEach( blockData -> { - BlockState baseState = Helpers.deserializeState(blockData.getBlockWithState()); - blockStates.addWeighted(baseState, blockData.getChance()); - }); - resolvedBlocks = true; - } - - public void resolveOtherData(DynamicRegistries dynamicRegistries) { - biomes = BiomeMatcher.buildFromData(biomeBlacklist, biomeWhitelist, dynamicRegistries); - dimensions = DimensionMatcher.buildFromData(dimensionList, dynamicRegistries); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java deleted file mode 100644 index 08cf048..0000000 --- a/src/main/java/com/mcmoddev/orespawn/loaders/ResourceLoader.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.mcmoddev.orespawn.loaders; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -import com.google.common.base.Joiner; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4API; -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.data.Constants.FileTypes; - -import static com.mcmoddev.orespawn.data.Config.COMMON; -import static com.mcmoddev.orespawn.utils.Helpers.*; - -import com.mcmoddev.orespawn.utils.Helpers; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import org.apache.commons.io.FileUtils; - - -public class ResourceLoader { - public void runLoaders() { - List foundFiles = new LinkedList<>(); - if (!COMMON.ignoreResources.get()) - ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) - .forEach(foundFiles::addAll); - - if (COMMON.extractToDisk.get()) { - for( ResourceLocation f : foundFiles) { - if (!OS4API.isKnownConfig(f)) { - extractFileToDisk(f); - OS4API.addKnownConfig(f); - } - } - } - - foundFiles.addAll(iterateDiskFiles()); - - List featuresFiles = foundFiles.stream().filter(rl -> rl.getPath().startsWith("features/")) - .collect(Collectors.toList()); - List replacementsFiles = foundFiles.stream() - .filter(rl -> rl.getPath().startsWith("replacements/")).collect(Collectors.toList()); - List presetsFiles = foundFiles.stream().filter(rl -> rl.getPath().startsWith("presets/")) - .collect(Collectors.toList()); - List spawnConfigs = foundFiles.stream().filter(rl -> !featuresFiles.contains(rl)) - .filter(rl -> !replacementsFiles.contains(rl)).filter(rl -> !presetsFiles.contains(rl)) - .collect(Collectors.toList()); - - OreSpawn.LOGGER.info("Found {} features files, {} replacements files, {} presets files and {} spawn configuration files", - featuresFiles.size(), replacementsFiles.size(), presetsFiles.size(), spawnConfigs.size()); - - OreSpawn.LOGGER.info("> Features Files:"); - featuresFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); - OreSpawn.LOGGER.info("> Replacements Files:"); - replacementsFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); - OreSpawn.LOGGER.info("> Presets Files:"); - presetsFiles.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); - OreSpawn.LOGGER.info("> Spawn Configs:"); - spawnConfigs.forEach(rl -> OreSpawn.LOGGER.info(">> {}", rl.toString())); - - - if (Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, Constants.FileBits.ALLOWED_MODS).toFile().exists()) { - OS4API.loadIntegrationWhitelist(); - } - - featuresFiles.stream() - .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) - .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.FEATURES)); - - replacementsFiles.stream() - .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) - .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.REPLACEMENTS)); - - presetsFiles.stream() - .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) - .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.PRESETS)); - - spawnConfigs.stream() - .filter(rl -> (OS4API.isAllowed(rl.getNamespace()) && OS4API.isAllowed(rl.getPath()+".json"))) - .forEach(rl -> loadResourceLocation(rl, Constants.FileTypes.SPAWN)); - } - - private void extractFileToDisk(ResourceLocation f) { - Path root = makePath(f, Constants.FileBits.RESOURCE); - Path targetRoot = Constants.JSONPATH.toAbsolutePath(); - - String resourceType = f.getPath().contains("/")?getType(f.getPath()):"config"; - String extractPath = resourceType.equals("config")?f.getNamespace()+"_"+f.getPath().replaceAll("/", "-"): - "config_data/"+resourceType+"/"+f.toString().replaceAll(":", "-").replaceAll("/", "_"); - try { - FileUtils.copyInputStreamToFile(Files.newInputStream(root), Paths.get(targetRoot.toString(), extractPath).toFile()); - } catch (IOException e) { - OreSpawn.LOGGER.error("Exception trying to copy the config {} out to the filesystem as required by the configuration settings: {}", f.toString(), e.getMessage()); - e.printStackTrace(); - } - } - - private String getType(String path) { - String p = path.split("/")[0]; - switch(p) { - case "features": - case "replacements": - case "presets": - return p.substring(0, p.length()-1); // strip the `s` off - default: - return "config"; - } - } - - private void loadResourceLocation(final ResourceLocation rl, final FileTypes type) { - if (rl.getNamespace().matches("orespawn-disk")) { - loadFromDisk(rl, type); - } else { - loadFromResource(rl, type); - } - } - - private List iterateFiles(ModFile modFile) { - try { - Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); - - return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds - filter(path -> path.toString().endsWith(".json")).map(path -> Joiner.on('/').join(path)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) - .collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } - - List iterateDiskFiles() { - try { - Path diskPath = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4).toAbsolutePath(); - return Files.walk(diskPath).map(path -> diskPath.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64).filter(path -> path.toString().endsWith(".json")) - .map(path -> Joiner.on('/').join(path)) - .map(path -> path.toLowerCase(Locale.US)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> String.format("orespawn-disk:%s",path)) - .map(Helpers::makeInternalResourceLocation).collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } - - private void loadFromDisk(ResourceLocation rl, FileTypes type) { - Path fp = makePath(rl, Constants.FileBits.DISK); - try { - switch(type) { - case FEATURES: - OS4API.loadFeaturesFromStream(Files.newInputStream(fp)); - break; - case PRESETS: - OS4API.loadPresetsFromStream(Files.newInputStream(fp)); - break; - case REPLACEMENTS: - OS4API.loadReplacementsFromStream(Files.newInputStream(fp)); - break; - case SPAWN: - OS4API.loadSpawnsFromStream(Files.newInputStream(fp)); - break; - default: - OreSpawn.LOGGER.error("Asked to load {} of type {} - I don't know this type of data", rl.toString(), type); - break; - - } - } catch (IOException e) { - OreSpawn.LOGGER.error("Exception loading resource {} -- {}", rl.toString(), e.getMessage()); - e.printStackTrace(); - } - } - - private void loadFromResource(ResourceLocation rl, FileTypes type) { - Path fp = makePath(rl, Constants.FileBits.RESOURCE); - try { - switch(type) { - case FEATURES: - OS4API.loadFeaturesFromStream(Files.newInputStream(fp)); - break; - case PRESETS: - OS4API.loadPresetsFromStream(Files.newInputStream(fp)); - break; - case REPLACEMENTS: - OS4API.loadReplacementsFromStream(Files.newInputStream(fp)); - break; - case SPAWN: - OS4API.loadSpawnsFromStream(Files.newInputStream(fp)); - break; - default: - OreSpawn.LOGGER.error("Asked to load {} of type {} - I don't know this type of data", rl.toString(), type); - break; - - } - } catch (IOException e) { - OreSpawn.LOGGER.error("Exception loading resource {} -- {}", rl.toString(), e.getMessage()); - e.printStackTrace(); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index 0d4f8ed..c335ce7 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -1,7 +1,6 @@ package com.mcmoddev.orespawn.utils; import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.BlockData; import com.mcmoddev.orespawn.data.Constants; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -74,14 +73,4 @@ public static Path makePath(ResourceLocation rl, String type) { } return result; } - - public static BlockState getBlockFor(BlockData blockData) { - try { - return new BlockStateParser(new StringReader(blockData.getBlockWithState()), false).parse(false).getState(); - } catch (CommandSyntaxException e) { - OreSpawn.LOGGER.error("Error parsing blockstate for {} -- {}", blockData.getBlockWithState(), e.getMessage()); - e.printStackTrace(); - return null; - } - } } From 1a286e9bbf622d113149e82b90f444501e38b1fc Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 10 Nov 2021 13:49:32 -0500 Subject: [PATCH 18/37] Get to work making the various allow/deny list stuff working in a simplified but functional manner. Refactors coming that will restore the buildability. --- .../mcmoddev/orespawn/data/BiomeMatcher.java | 67 ++-- .../com/mcmoddev/orespawn/data/BlockType.java | 71 ++++ .../com/mcmoddev/orespawn/data/Config.java | 35 -- .../data/DefaultFeatureParameters.java | 4 + .../orespawn/data/DimensionMatcher.java | 87 +++-- .../orespawn/utils/ReflectionHelper.java | 3 +- .../utils/codecs/BiomeMatcherConfig.java | 30 ++ .../utils/codecs/BlockMatcherConfig.java | 27 ++ .../utils/codecs/BlockTypeConfig.java | 21 ++ .../DefaultFeatureParametersConfig.java | 28 ++ .../utils/codecs/DimensionMatcherConfig.java | 27 ++ .../world/gen/DefaultFeatureConfig.java | 35 -- .../gen/configs/DefaultFeatureConfig.java | 46 +++ .../orespawn4-data/features/features.json | 26 -- .../assets/orespawn4-data/orespawn.json | 332 ------------------ .../replacements/replacements.json | 8 - 16 files changed, 344 insertions(+), 503 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/data/BlockType.java create mode 100644 src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java delete mode 100644 src/main/resources/assets/orespawn4-data/features/features.json delete mode 100644 src/main/resources/assets/orespawn4-data/orespawn.json delete mode 100644 src/main/resources/assets/orespawn4-data/replacements/replacements.json diff --git a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java index 9fe1a96..2a0b74f 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java @@ -1,13 +1,14 @@ package com.mcmoddev.orespawn.data; -import com.sun.jndi.rmi.registry.RegistryContextFactory; +import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.DynamicRegistries; -import net.minecraft.util.registry.MutableRegistry; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -16,43 +17,51 @@ public class BiomeMatcher { private final List> allowedBiomes = new LinkedList<>(); - public static BiomeMatcher buildFromData(List biomeBlacklist, List biomeWhitelist, DynamicRegistries dynamicRegistries) { - BiomeMatcher result = new BiomeMatcher(); - MutableRegistry registry = dynamicRegistries.getRegistry(Registry.BIOME_KEY); - if (biomeWhitelist.size() == 0 || biomeWhitelist.contains("orespawn4:any")) - result.defaultLoad(registry); - else - result.loadWhitelist(biomeWhitelist); - - if (biomeBlacklist.size() > 0) - result.clearBlacklistedBits(biomeBlacklist); - - return result; - } + private final BiomeMatcherConfig myConfig; + private final ResourceLocation type; + + private static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); + private static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); + private static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); + private static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); - public boolean biomeMatches(final ResourceLocation biomeRL) { - return biomeMatches(RegistryKey.getOrCreateKey(Registry.BIOME_KEY, biomeRL)); + public BiomeMatcher(final BiomeMatcherConfig config) { + myConfig = config; + type = config.type; + allowedBiomes.addAll(config.data.stream().map( rl -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, rl)).collect(Collectors.toList())); } - public boolean biomeMatches(RegistryKey biomeKey) { - return allowedBiomes.contains(biomeKey); + public boolean matches(final String biomeName) { + if (type.equals(denyall) || (allowedBiomes.isEmpty() && type.equals(whitelist))) return false; + else if (type.equals(allowall) || (allowedBiomes.isEmpty() && type.equals(blacklist))) return true; + else return matches(makeResourceLocation(biomeName)); } - public boolean biomeMatches(final String biomeName) { - return biomeMatches(makeBlockResourceLocation(biomeName)); + private ResourceLocation makeResourceLocation(final String name) { + String namespace = "minecraft"; + String biomeId = name; + + if (name.indexOf(':') > -1) { + String bits[] = name.split(":"); + namespace = bits[0]; + biomeId = bits[1]; + } + + return new ResourceLocation(namespace, biomeId); } - private void clearBlacklistedBits(List biomeBlacklist) { - biomeBlacklist.stream().map( name -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, makeBlockResourceLocation(name))) - .filter( biomeKey -> allowedBiomes.contains(biomeKey)) - .forEach( biomeKey -> allowedBiomes.remove(biomeKey)); + public boolean matches(final ResourceLocation biomeName) { + return matches(RegistryKey.getOrCreateKey(Registry.BIOME_KEY, biomeName)); } - private void loadWhitelist(List biomeWhitelist) { - allowedBiomes.addAll( biomeWhitelist.stream().map( name -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, makeBlockResourceLocation(name))).collect(Collectors.toList())); + private boolean matches(final RegistryKey biome) { + if (type == blacklist && allowedBiomes.contains(biome)) return false; + else if (type == whitelist && allowedBiomes.contains(biome)) return true; + + return false; } - private void defaultLoad(MutableRegistry registry) { - allowedBiomes.addAll( registry.getEntries().stream().map( entry -> entry.getKey()).collect(Collectors.toList()) ); + public BiomeMatcherConfig getConfig() { + return myConfig; } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/BlockType.java b/src/main/java/com/mcmoddev/orespawn/data/BlockType.java new file mode 100644 index 0000000..456f86a --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/BlockType.java @@ -0,0 +1,71 @@ +package com.mcmoddev.orespawn.data; + +import com.mcmoddev.orespawn.utils.codecs.BlockTypeConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.minecraft.util.ResourceLocation; + +import java.util.Locale; + +public class BlockType { + private final BlockTypeType type; + private final ResourceLocation blockLoc; + + public BlockType(final BlockTypeConfig conf) { + this.type = conf.type; + this.blockLoc = conf.name; + } + + public BlockTypeType getType() { + return this.type; + } + + public ResourceLocation getName() { + return this.blockLoc; + } + + public enum BlockTypeType { + TAG("TAG"), BLOCK("BLOCK"); + + + public static final Codec CODEC = Codec.STRING.comapFlatMap(BlockTypeType::myValueOf, BlockTypeType::asString).stable(); + + private final String text; + + /** + * @param text + */ + BlockTypeType(final String text) { + this.text = text; + } + + public static DataResult myValueOf(final String value) { + final String lookup = value.toUpperCase(Locale.US); + DataResult res; + + switch(lookup) { + case "TAG": + res = DataResult.success(TAG); + break; + case "BLOCK": + res = DataResult.success(BLOCK); + break; + default: + res = DataResult.error(String.format("Value %s is not valid for a type flag here", lookup)); + } + return res; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return text; + } + + public static String asString( BlockTypeType val ) { + return val.toString(); + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java index fb2f859..42f9f4f 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Config.java @@ -15,35 +15,12 @@ public class Config { public static final CommonConfig COMMON; public static class CommonConfig { - public final ForgeConfigSpec.BooleanValue replaceVanillaOreGeneration; - public final ForgeConfigSpec.BooleanValue replaceAllGeneration; - public final ForgeConfigSpec.BooleanValue enableRetrogeneration; - public final ForgeConfigSpec.BooleanValue forceEnableRetrogeneration; public final ForgeConfigSpec.BooleanValue flattenBedrock; public final ForgeConfigSpec.BooleanValue retroactivelyFlattenBedrock; public final ForgeConfigSpec.IntValue layersOfBedrock; - public final ForgeConfigSpec.BooleanValue extractToDisk; - public final ForgeConfigSpec.BooleanValue ignoreResources; - public final ForgeConfigSpec.BooleanValue ignoreDisk; CommonConfig(ForgeConfigSpec.Builder builder) { builder.push("general"); - replaceVanillaOreGeneration = builder - .comment("Attempt to override vanilla Minecraft ore generation when TRUE") - .translation("text.mmd_orespawn.config.replace_vanilla") - .define("Replace Vanilla Oregen", false); - replaceAllGeneration = builder - .comment("Attempt to replace all ore generation, even from other mods, when TRUE") - .translation("text.mmd_orespawn.config.replace_all") - .define("Replace All Generation", false); - enableRetrogeneration = builder - .comment("Attempt to generate new spawns in chunks that were previously generated or were generated with different configuration options when TRUE") - .translation("text.mmd_orespawn.config.retrogen") - .define("Retrogen", false); - forceEnableRetrogeneration = builder - .comment("Force retroactive generation of new spawns, even if the feature is configured to not perform it when TRUE") - .translation("text.mmd_orespawn.config.force_retrogen") - .define("Force Retrogen", false); flattenBedrock = builder .comment("Make the bedrock flat in chunks generated when this option is TRUE") .translation("text.mmd_orespawn.config.flatten_bedrock") @@ -56,18 +33,6 @@ public static class CommonConfig { .comment("Attempt flatten the bedrock in chunks generated before this option and the \"Flatten Bedrock\" option were set to TRUE") .translation("text.mmd_orespawn.config.retro_bedrock") .define("Retrogen Flat Bedrock", false); - extractToDisk = builder - .comment("Extract all integration configurations found to \"config/mmd-orespawn-4\" when TRUE") - .translation("text.mmd_orespawn.config.extract_integration") - .define("Extract Files", false); - ignoreResources = builder - .comment("Do not attempt to locate or use any integration configurations when TRUE") - .translation("text.mmd_orespawn.config.ignore_integration") - .define("Ignore Integration", false); - ignoreDisk = builder - .comment("Do not attempt to load any config files that are on disk when TRUE") - .translation("text.mmd_orespawn.config.ignore_disk") - .define("Ignore Config Files On Disk", false); } } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java b/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java new file mode 100644 index 0000000..0f413ae --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java @@ -0,0 +1,4 @@ +package com.mcmoddev.orespawn.data; + +public class DefaultFeatureParameters { +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java index 9827a73..74213a1 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java @@ -1,62 +1,75 @@ package com.mcmoddev.orespawn.data; -import com.mcmoddev.orespawn.utils.Helpers; +import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.DynamicRegistries; -import net.minecraft.util.registry.MutableRegistry; import net.minecraft.util.registry.Registry; import net.minecraft.world.Dimension; -import net.minecraft.world.DimensionType; -import net.minecraft.world.World; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; +import static com.mcmoddev.orespawn.utils.Helpers.makeBlockResourceLocation; + public class DimensionMatcher { private final List> allowedDimensions = new LinkedList<>(); - public static DimensionMatcher buildFromData(List dimensionList, DynamicRegistries dynamicRegistries) { - DimensionMatcher result = new DimensionMatcher(); - MutableRegistry registry = dynamicRegistries.getRegistry(Registry.DIMENSION_KEY); - DimensionType overWorldType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.OVERWORLD).getDimensionType(); - DimensionType netherType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.THE_NETHER).getDimensionType(); - DimensionType endType = dynamicRegistries.getRegistry(Registry.WORLD_KEY).getValueForKey(World.THE_END).getDimensionType(); - - if (dimensionList.contains("orespawn4:overworlds")) - result.loadMatching(overWorldType, registry); - if (dimensionList.contains("orespawn4:nethers")) - result.loadMatching(netherType, registry); - if (dimensionList.contains("orespawn4:ends")) - result.loadMatching(endType, registry); - - if (dimensionList.size() > 0) - result.loadList(dimensionList); - - return result; + private final DimensionMatcherConfig myConfig; + private boolean whitelistEmpty = false; + private boolean blacklistEmpty = false; + private boolean overworlds = false; + private boolean voids = false; + private boolean hells = false; + + private static final class internal { + public static final ResourceLocation OVERWORLDS = new ResourceLocation("orespawn4", "overworlds"); + public static final ResourceLocation NETHERS = new ResourceLocation("orespawn4", "nethers"); + public static final ResourceLocation ENDS = new ResourceLocation("orespawn4", "ends"); } - public boolean matches(final String name) { - return matches(Helpers.makeBlockResourceLocation(name)); + public DimensionMatcher(final DimensionMatcherConfig config) { + myConfig = config; + + if (config.whitelist.isEmpty()) whitelistEmpty = true; + if (config.blacklist.isEmpty()) blacklistEmpty = true; + + if (whitelistEmpty && blacklistEmpty) return; + + if (!whitelistEmpty) + if (config.whitelist.contains(internal.OVERWORLDS)) + overworlds = true; + else if (config.whitelist.contains(internal.NETHERS)) + hells = true; + else if (config.whitelist.contains(internal.ENDS)) + voids = true; + else + allowedDimensions.addAll(config.whitelist.stream().map(rl -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, rl)).collect(Collectors.toList())); + else + if (!blacklistEmpty) + if (config.blacklist.contains(internal.OVERWORLDS)) + overworlds = true; + else if (config.blacklist.contains(internal.NETHERS)) + hells = true; + else if (config.blacklist.contains(internal.ENDS)) + voids = true; + else + allowedDimensions.addAll(config.blacklist.stream().map(rl -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, rl)).collect(Collectors.toList())); } - public boolean matches(final ResourceLocation resourceLocation) { - return matches(RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, resourceLocation)); + public boolean matches(final String dimensionName) { + return false; } - public boolean matches(RegistryKey registryKey) { - return allowedDimensions.contains(registryKey); + public boolean matches(final ResourceLocation dimensionName) { + return false; } - private void loadList(List dimensionList) { - allowedDimensions.addAll(dimensionList.stream().filter(name -> !name.startsWith("orespawn4:")) - .map( Helpers::makeBlockResourceLocation ) - .map( resourceLocation -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, resourceLocation) ) - .collect(Collectors.toList())); + private boolean matches(final RegistryKey dimensionName) { + return false; } - private void loadMatching(DimensionType matchType, MutableRegistry registry) { - allowedDimensions.addAll(registry.getEntries().stream().filter(entry -> entry.getValue().getDimensionType().isSame(matchType)).map(Map.Entry::getKey).collect(Collectors.toList())); + public DimensionMatcherConfig getConfig() { + return myConfig; } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java b/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java index 92ccbcd..485d0f6 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java @@ -1,12 +1,13 @@ package com.mcmoddev.orespawn.utils; import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.api.OS4Feature; +//import com.mcmoddev.orespawn.api.OS4Feature; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectionHelper { + public class OS4Feature {}; public static Class getFeatureNamed(final String name, final String fqcn) { Class resultBase; Class result = null; diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java new file mode 100644 index 0000000..89cb0be --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java @@ -0,0 +1,30 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import com.google.common.collect.ImmutableList; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.gen.feature.OreFeature; + +import java.util.List; + +public class BiomeMatcherConfig { + public final List data; + public final ResourceLocation type; + + public static final Codec CODEC = RecordCodecBuilder.create((base) -> { + return base.group(ResourceLocation.CODEC.listOf().fieldOf("entries").forGetter((config) -> { + return config.data; + }), + ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { + return config.type; + })).apply(base, BiomeMatcherConfig::new); + }); + + public BiomeMatcherConfig(final List entryList, final ResourceLocation type) { + this.data = ImmutableList.copyOf(entryList); + this.type = type; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java new file mode 100644 index 0000000..2afed0a --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java @@ -0,0 +1,27 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import com.mcmoddev.orespawn.data.BlockType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class BlockMatcherConfig { + public static final Codec CODEC = RecordCodecBuilder.create((base) -> { + return base.group(BlockTypeConfig.CODEC.listOf().fieldOf("blocks").forGetter((config) -> config.asTypeConfig())).apply(base, BlockMatcherConfig::new); + }); + + public final List blocks; + + public BlockMatcherConfig(final List blocksIn) { + this.blocks = new LinkedList<>(); + + this.blocks.addAll(blocksIn.stream().map(BlockType::new).collect(Collectors.toList())); + } + + public List asTypeConfig() { + return this.blocks.stream().map( bt -> new BlockTypeConfig(bt.getType(), bt.getName())).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java new file mode 100644 index 0000000..656607d --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java @@ -0,0 +1,21 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import com.mcmoddev.orespawn.data.BlockType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.ResourceLocation; + +public class BlockTypeConfig { + public static final Codec CODEC = RecordCodecBuilder.create((base) -> { + return base.group(BlockType.BlockTypeType.CODEC.fieldOf("type").forGetter((config) -> config.type), + ResourceLocation.CODEC.fieldOf("name").forGetter((config) -> config.name)).apply(base, BlockTypeConfig::new); + }); + + public final BlockType.BlockTypeType type; + public final ResourceLocation name; + + public BlockTypeConfig(final BlockType.BlockTypeType refType, final ResourceLocation refID) { + this.type = refType; + this.name = refID; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java new file mode 100644 index 0000000..930947d --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java @@ -0,0 +1,28 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import com.electronwill.nightconfig.core.conversion.InvalidValueException; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public class DefaultFeatureParametersConfig { + public static final Codec CODEC = RecordCodecBuilder.create((base) -> { + return base.group(Codec.intRange(0, 255).fieldOf("size").forGetter((config) -> config.size), + Codec.intRange(0, 255).fieldOf("minHeight").forGetter((config) -> config.minHeight), + Codec.intRange(0, 255).fieldOf("maxHeight").forGetter((config) -> config.minHeight), + Codec.floatRange(0, 1).fieldOf("frequency").forGetter((config) -> config.frequency)).apply(base, DefaultFeatureParametersConfig::new); + }); + + public final int size; + public final float frequency; + public final int minHeight; + public final int maxHeight; + + public DefaultFeatureParametersConfig(final int size, final int minHeight, final int maxHeight, final float frequency) { + if (minHeight > maxHeight) + throw new InvalidValueException("Minimum Height must be less than or equal to Maximum Height"); + this.size = size; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + this.frequency = frequency; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java new file mode 100644 index 0000000..d0fc836 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java @@ -0,0 +1,27 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.ResourceLocation; + +import java.util.List; + +public class DimensionMatcherConfig { + public final List data; + public final ResourceLocation type; + + public static final Codec CODEC = RecordCodecBuilder.create((base) -> { + return base.group(ResourceLocation.CODEC.listOf().fieldOf("entries").forGetter((config) -> { + return config.data; + }), + ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { + return config.type; + })).apply(base, DimensionMatcherConfig::new); + }); + + public DimensionMatcherConfig(final List entryList, final ResourceLocation type) { + this.data = ImmutableList.copyOf(entryList); + this.type = type; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java deleted file mode 100644 index 3ebc3e0..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/DefaultFeatureConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.mcmoddev.orespawn.world.gen; - -import com.electronwill.nightconfig.core.conversion.InvalidValueException; -import com.mcmoddev.orespawn.data.BiomeMatcher; -import com.mcmoddev.orespawn.data.DimensionMatcher; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.block.BlockState; -import net.minecraft.block.pattern.BlockMatcher; -import net.minecraft.util.WeightedList; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.IFeatureConfig; - -import java.util.stream.Stream; - -public class DefaultFeatureConfig implements IFeatureConfig { - public final BlockMatcher matcher; - public final int size; - public final WeightedList blocks; - public final int minHeight; - public final int maxHeight; - public final BiomeMatcher biomeMatch; - public final DimensionMatcher dimensionMatch; - - public DefaultFeatureConfig(BlockMatcher repl, WeightedList blocks, int sz, int minY, int maxY, BlockMatcher blockMatcher, BiomeMatcher biomeMatcher, DimensionMatcher dimensionMatcher) { - this.matcher = repl; - this.size = sz; - this.blocks = blocks; - this.minHeight = minY; - this.maxHeight = maxY; - this.biomeMatch = biomeMatcher; - this.dimensionMatch = dimensionMatcher; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java new file mode 100644 index 0000000..4a1b74f --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -0,0 +1,46 @@ +package com.mcmoddev.orespawn.world.gen.configs; + +import com.mcmoddev.orespawn.data.BiomeMatcher; +import com.mcmoddev.orespawn.data.DefaultFeatureParameters; +import com.mcmoddev.orespawn.data.DimensionMatcher; +import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; +import com.mcmoddev.orespawn.utils.codecs.BlockMatcherConfig; +import com.mcmoddev.orespawn.utils.codecs.DefaultFeatureParametersConfig; +import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.BlockState; +import net.minecraft.block.pattern.BlockMatcher; +import net.minecraft.util.WeightedList; +import net.minecraft.world.gen.feature.IFeatureConfig; + +public class DefaultFeatureConfig implements IFeatureConfig { + public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { + return codec.group( + Codec.STRING.fieldOf("feature").forGetter((config) -> config.feature), + BlockMatcherConfig.CODEC.fieldOf("replaces").forGetter((config) -> config.replacer), + DefaultFeatureParametersConfig.CODEC.fieldOf("parameters").forGetter((config) -> config.parameters), + BiomeMatcherConfig.CODEC.fieldOf("biomes").forGetter((config) -> config.biomeMatch), + DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch.getConfig()), + WeightedList.getCodec(BlockState.CODEC).fieldOf("blocks").forGetter((config) -> config.blocks) + ).apply(codec, DefaultFeatureConfig::new); + }); + + public final WeightedList blocks; + public final BiomeMatcherConfig biomeMatch; + public final DimensionMatcher dimensionMatch; + public final DefaultFeatureParametersConfig parameters; + public final String feature; + public final BlockMatcherConfig replacer; + + public DefaultFeatureConfig(String featureName, BlockMatcherConfig replacement, + DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, + DimensionMatcher dimensions, WeightedList blocks ) { + this.replacer = replacement; + this.blocks = blocks; + this.biomeMatch = biomes; + this.dimensionMatch = dimensions; + this.parameters = parameters; + this.feature = featureName; + } +} diff --git a/src/main/resources/assets/orespawn4-data/features/features.json b/src/main/resources/assets/orespawn4-data/features/features.json deleted file mode 100644 index 09075a3..0000000 --- a/src/main/resources/assets/orespawn4-data/features/features.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "name": "default", - "class": "com.mcmoddev.orespawn.impl.features.DefaultFeatureGenerator" - }, - { - "name": "vein", - "class": "com.mcmoddev.orespawn.impl.features.VeinGenerator" - }, - { - "name": "normal-cloud", - "class": "com.mcmoddev.orespawn.impl.features.NormalCloudGenerator" - }, - { - "name": "precision", - "class": "com.mcmoddev.orespawn.impl.features.PrecisionGenerator" - }, - { - "name": "clusters", - "class": "com.mcmoddev.orespawn.impl.features.ClusterGenerator" - }, - { - "name": "underfluids", - "class": "com.mcmoddev.orespawn.impl.features.UnderFluid" - } -] \ No newline at end of file diff --git a/src/main/resources/assets/orespawn4-data/orespawn.json b/src/main/resources/assets/orespawn4-data/orespawn.json deleted file mode 100644 index 4021c4f..0000000 --- a/src/main/resources/assets/orespawn4-data/orespawn.json +++ /dev/null @@ -1,332 +0,0 @@ -{ - "version": "2.0", - "spawns": { - "quartz_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [ - -1 - ], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 15, - "variation": 4, - "frequency": 7, - "minHeight": 0, - "maxHeight": 128 - }, - "blocks": [ - { - "name": "minecraft:quartz_ore", - "chance": 100 - } - ] - }, - "coal_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 25, - "variation": 12, - "frequency": 20, - "minHeight": 0, - "maxHeight": 128 - }, - "blocks": [ - { - "name": "minecraft:coal_ore", - "chance": 100 - } - ] - }, - "iron_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 8, - "variation": 4, - "frequency": 20, - "minHeight": 0, - "maxHeight": 64 - }, - "blocks": [ - { - "name": "minecraft:iron_ore", - "chance": 100 - } - ] - }, - "gold_ore_standard": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 8, - "variation": 2, - "frequency": 2, - "minHeight": 0, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:gold_ore", - "chance": 100 - } - ] - }, - "gold_ore_mesa": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "includes": [ "MESA" ] - }, - "parameters": { - "size": 8, - "variation": 2, - "frequency": 2, - "minHeight": 32, - "maxHeight": 79 - }, - "blocks": [ - { - "name": "minecraft:gold_ore", - "chance": 100 - } - ] - }, - "diamond_ore": { - "retrogen": false, - "enabled": true, - "feature": "precision", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 3, - "numObjects": 2, - "minHeight": 0, - "maxHeight": 16 - }, - "blocks": [ - { - "name": "minecraft:diamond_ore", - "chance": 100 - } - ] - }, - "redstone_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 6, - "variation": 3, - "frequency": 8, - "minHeight": 0, - "maxHeight": 16 - }, - "blocks": [ - { - "name": "minecraft:redstone_ore", - "chance": 100 - } - ] - }, - "lapis_ore": { - "retrogen": false, - "enabled": false, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 5, - "variation": 2, - "frequency": 1, - "minHeight": 0, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:lapis_ore", - "chance": 100 - } - ] - }, - "emerald_ore": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "includes": [ "MOUNTAIN" ] - }, - "parameters": { - "size": 1, - "variation": 0, - "frequency": 8, - "minHeight": 4, - "maxHeight": 32 - }, - "blocks": [ - { - "name": "minecraft:emerald_ore", - "chance": 100 - } - ] - }, - "dirt": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:dirt", - "chance": 100, - "state": "snowy=false,variant=dirt" - } - ] - }, - "gravel": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 8, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:gravel", - "chance": 100 - } - ] - }, - "andesite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=andesite" - } - ] - }, - "diorite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=diorite" - } - ] - }, - "granite": { - "retrogen": false, - "enabled": true, - "feature": "default", - "replaces": "default", - "dimensions": [], - "biomes": { - "excludes": [] - }, - "parameters": { - "size": 112, - "variation": 50, - "frequency": 10, - "minHeight": 0, - "maxHeight": 255 - }, - "blocks": [ - { - "name": "minecraft:stone", - "chance": 100, - "state": "variant=granite" - } - ] - } - } -} \ No newline at end of file diff --git a/src/main/resources/assets/orespawn4-data/replacements/replacements.json b/src/main/resources/assets/orespawn4-data/replacements/replacements.json deleted file mode 100644 index 8e378cf..0000000 --- a/src/main/resources/assets/orespawn4-data/replacements/replacements.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "default": [ { "name": "minecraft:stone" }, - { "name": "minecraft:andesite" }, - { "name": "minecraft:diorite" }, - { "name": "minecraft:granite" }, - { "name": "minecraft:netherrack" }, - { "name": "minecraft:end_stone" } ] -} From dfec3880e8a73c802d70bcecfb06e6accb655d4c Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 10 Nov 2021 14:04:20 -0500 Subject: [PATCH 19/37] Spread the love - the new allow/deny list base class is complete and implemented --- .../mcmoddev/orespawn/data/BiomeMatcher.java | 53 ++----------------- .../orespawn/data/DimensionMatcher.java | 53 ++----------------- .../utils/codecs/AllowDenyListBase.java | 52 ++++++++++++++++++ 3 files changed, 58 insertions(+), 100 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java diff --git a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java index 2a0b74f..5bda486 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java @@ -1,64 +1,17 @@ package com.mcmoddev.orespawn.data; -import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.utils.codecs.AllowDenyListBase; import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.DynamicRegistries; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import static com.mcmoddev.orespawn.utils.Helpers.makeBlockResourceLocation; - -public class BiomeMatcher { - private final List> allowedBiomes = new LinkedList<>(); +public class BiomeMatcher extends AllowDenyListBase>> { private final BiomeMatcherConfig myConfig; - private final ResourceLocation type; - - private static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); - private static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); - private static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); - private static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); public BiomeMatcher(final BiomeMatcherConfig config) { + super( config.type, Registry.BIOME_KEY, config.data ); myConfig = config; - type = config.type; - allowedBiomes.addAll(config.data.stream().map( rl -> RegistryKey.getOrCreateKey(Registry.BIOME_KEY, rl)).collect(Collectors.toList())); - } - - public boolean matches(final String biomeName) { - if (type.equals(denyall) || (allowedBiomes.isEmpty() && type.equals(whitelist))) return false; - else if (type.equals(allowall) || (allowedBiomes.isEmpty() && type.equals(blacklist))) return true; - else return matches(makeResourceLocation(biomeName)); - } - - private ResourceLocation makeResourceLocation(final String name) { - String namespace = "minecraft"; - String biomeId = name; - - if (name.indexOf(':') > -1) { - String bits[] = name.split(":"); - namespace = bits[0]; - biomeId = bits[1]; - } - - return new ResourceLocation(namespace, biomeId); - } - - public boolean matches(final ResourceLocation biomeName) { - return matches(RegistryKey.getOrCreateKey(Registry.BIOME_KEY, biomeName)); - } - - private boolean matches(final RegistryKey biome) { - if (type == blacklist && allowedBiomes.contains(biome)) return false; - else if (type == whitelist && allowedBiomes.contains(biome)) return true; - - return false; } public BiomeMatcherConfig getConfig() { diff --git a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java index 74213a1..967e99c 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java +++ b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java @@ -1,5 +1,6 @@ package com.mcmoddev.orespawn.data; +import com.mcmoddev.orespawn.utils.codecs.AllowDenyListBase; import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; @@ -13,60 +14,12 @@ import static com.mcmoddev.orespawn.utils.Helpers.makeBlockResourceLocation; -public class DimensionMatcher { - private final List> allowedDimensions = new LinkedList<>(); +public class DimensionMatcher extends AllowDenyListBase>> { private final DimensionMatcherConfig myConfig; - private boolean whitelistEmpty = false; - private boolean blacklistEmpty = false; - private boolean overworlds = false; - private boolean voids = false; - private boolean hells = false; - - private static final class internal { - public static final ResourceLocation OVERWORLDS = new ResourceLocation("orespawn4", "overworlds"); - public static final ResourceLocation NETHERS = new ResourceLocation("orespawn4", "nethers"); - public static final ResourceLocation ENDS = new ResourceLocation("orespawn4", "ends"); - } public DimensionMatcher(final DimensionMatcherConfig config) { + super(config.type, Registry.DIMENSION_KEY, config.data); myConfig = config; - - if (config.whitelist.isEmpty()) whitelistEmpty = true; - if (config.blacklist.isEmpty()) blacklistEmpty = true; - - if (whitelistEmpty && blacklistEmpty) return; - - if (!whitelistEmpty) - if (config.whitelist.contains(internal.OVERWORLDS)) - overworlds = true; - else if (config.whitelist.contains(internal.NETHERS)) - hells = true; - else if (config.whitelist.contains(internal.ENDS)) - voids = true; - else - allowedDimensions.addAll(config.whitelist.stream().map(rl -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, rl)).collect(Collectors.toList())); - else - if (!blacklistEmpty) - if (config.blacklist.contains(internal.OVERWORLDS)) - overworlds = true; - else if (config.blacklist.contains(internal.NETHERS)) - hells = true; - else if (config.blacklist.contains(internal.ENDS)) - voids = true; - else - allowedDimensions.addAll(config.blacklist.stream().map(rl -> RegistryKey.getOrCreateKey(Registry.DIMENSION_KEY, rl)).collect(Collectors.toList())); - } - - public boolean matches(final String dimensionName) { - return false; - } - - public boolean matches(final ResourceLocation dimensionName) { - return false; - } - - private boolean matches(final RegistryKey dimensionName) { - return false; } public DimensionMatcherConfig getConfig() { diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java new file mode 100644 index 0000000..7af2d70 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java @@ -0,0 +1,52 @@ +package com.mcmoddev.orespawn.utils.codecs; + +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class AllowDenyListBase { + private final List listed = new LinkedList<>(); + private final ResourceLocation type; + private final T baseKey; + + private static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); + private static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); + private static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); + private static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); + + protected AllowDenyListBase(final ResourceLocation matchType, final T key, List baseList) { + this.type = matchType; + this.baseKey = key; + listed.addAll(baseList.stream().map( rl -> RegistryKey.getOrCreateKey(this.baseKey, rl)).collect(Collectors.toList())); + } + + public boolean matches(final String biomeName) { + if (type.equals(denyall) || (listed.isEmpty() && type.equals(whitelist))) return false; + else if (type.equals(allowall) || (listed.isEmpty() && type.equals(blacklist))) return true; + else return matches(makeResourceLocation(biomeName)); + } + + private ResourceLocation makeResourceLocation(final String name) { + String namespace = "minecraft"; + String biomeId = name; + + if (name.indexOf(':') > -1) { + String[] bits = name.split(":"); + namespace = bits[0]; + biomeId = bits[1]; + } + + return new ResourceLocation(namespace, biomeId); + } + + public boolean matches(final ResourceLocation biomeName) { + return matches(RegistryKey.getOrCreateKey(this.baseKey, biomeName)); + } + + private boolean matches(final RegistryKey item) { + if (type == blacklist && listed.contains(item)) return false; + return listed.contains(item); + } +} From ca5463caf424f087905e5b49d800cbb6a451222e Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 10 Nov 2021 14:07:52 -0500 Subject: [PATCH 20/37] Small mistakes in implementation of the generic base fixed... now to just figure out WTF is going on with the `DefaultFeatureConfig` --- .../mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java index 7af2d70..0a33953 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; public class AllowDenyListBase { - private final List listed = new LinkedList<>(); + private final List listed = new LinkedList<>(); private final ResourceLocation type; private final T baseKey; @@ -19,7 +19,7 @@ public class AllowDenyListBase { protected AllowDenyListBase(final ResourceLocation matchType, final T key, List baseList) { this.type = matchType; this.baseKey = key; - listed.addAll(baseList.stream().map( rl -> RegistryKey.getOrCreateKey(this.baseKey, rl)).collect(Collectors.toList())); + listed.addAll((List) baseList.stream().map( rl -> (T) RegistryKey.getOrCreateKey(this.baseKey, rl)).collect(Collectors.toList())); } public boolean matches(final String biomeName) { @@ -42,10 +42,10 @@ private ResourceLocation makeResourceLocation(final String name) { } public boolean matches(final ResourceLocation biomeName) { - return matches(RegistryKey.getOrCreateKey(this.baseKey, biomeName)); + return matches((T)RegistryKey.getOrCreateKey(this.baseKey, biomeName)); } - private boolean matches(final RegistryKey item) { + private boolean matches(final T item) { if (type == blacklist && listed.contains(item)) return false; return listed.contains(item); } From 64e075a9001b33b9992358abcd2e907e8bdf9ac2 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 10 Nov 2021 15:13:20 -0500 Subject: [PATCH 21/37] Fix an overlooked bit that needed changed... the DefaultFeatureConfig takes all `Config` parameters (and a `String`) - it does not take a concrete `DimensionMatcher`... --- .../orespawn/world/gen/configs/DefaultFeatureConfig.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index 4a1b74f..e4d85b4 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -13,6 +13,7 @@ import net.minecraft.block.pattern.BlockMatcher; import net.minecraft.util.WeightedList; import net.minecraft.world.gen.feature.IFeatureConfig; +import org.lwjgl.system.Pointer; public class DefaultFeatureConfig implements IFeatureConfig { public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { @@ -21,21 +22,21 @@ public class DefaultFeatureConfig implements IFeatureConfig { BlockMatcherConfig.CODEC.fieldOf("replaces").forGetter((config) -> config.replacer), DefaultFeatureParametersConfig.CODEC.fieldOf("parameters").forGetter((config) -> config.parameters), BiomeMatcherConfig.CODEC.fieldOf("biomes").forGetter((config) -> config.biomeMatch), - DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch.getConfig()), + DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch), WeightedList.getCodec(BlockState.CODEC).fieldOf("blocks").forGetter((config) -> config.blocks) ).apply(codec, DefaultFeatureConfig::new); }); public final WeightedList blocks; public final BiomeMatcherConfig biomeMatch; - public final DimensionMatcher dimensionMatch; + public final DimensionMatcherConfig dimensionMatch; public final DefaultFeatureParametersConfig parameters; public final String feature; public final BlockMatcherConfig replacer; public DefaultFeatureConfig(String featureName, BlockMatcherConfig replacement, DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, - DimensionMatcher dimensions, WeightedList blocks ) { + DimensionMatcherConfig dimensions, WeightedList blocks ) { this.replacer = replacement; this.blocks = blocks; this.biomeMatch = biomes; From ff71da56c3a72eb34560e51baeb7da33cf2ccfff Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Wed, 10 Nov 2021 21:25:12 -0500 Subject: [PATCH 22/37] Skeleton of the actual "default" feature as was done by OreSpawn 1 - still has a lot of code to be added... --- .../utils/codecs/BiomeMatcherConfig.java | 2 +- .../utils/codecs/DimensionMatcherConfig.java | 2 +- .../world/gen/configs/DefaultFeature.java | 44 +++++++++++++++++++ .../gen/configs/DefaultFeatureConfig.java | 9 ++-- .../world/gen/configs/OreSpawnFeature.java | 25 +++++++++++ 5 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java create mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java index 89cb0be..ab7d00a 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java @@ -15,7 +15,7 @@ public class BiomeMatcherConfig { public final ResourceLocation type; public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(ResourceLocation.CODEC.listOf().fieldOf("entries").forGetter((config) -> { + return base.group(ResourceLocation.CODEC.listOf().fieldOf("data").forGetter((config) -> { return config.data; }), ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java index d0fc836..d36db9b 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java @@ -12,7 +12,7 @@ public class DimensionMatcherConfig { public final ResourceLocation type; public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(ResourceLocation.CODEC.listOf().fieldOf("entries").forGetter((config) -> { + return base.group(ResourceLocation.CODEC.listOf().fieldOf("data").forGetter((config) -> { return config.data; }), ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java new file mode 100644 index 0000000..59d2960 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java @@ -0,0 +1,44 @@ +package com.mcmoddev.orespawn.world.gen.configs; + +import com.mcmoddev.orespawn.data.BiomeMatcher; +import com.mcmoddev.orespawn.data.DimensionMatcher; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ISeedReader; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.Feature; + +import javax.annotation.Nonnull; +import java.util.Random; + +public class DefaultFeature extends OreSpawnFeature { + private final DefaultFeatureConfig myConfig; + private final BiomeMatcher biomeMatcher; + private final DimensionMatcher dimensionMatcher; + + public DefaultFeature(DefaultFeatureConfig config) { + super(config, Feature.ORE); + myConfig = config; + biomeMatcher = new BiomeMatcher(config.biomeMatch); + dimensionMatcher = new DimensionMatcher(config.dimensionMatch); + } + + @Override + public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos) { + if (myConfig.parameters.frequency <= rand.nextFloat()) { + ResourceLocation b = reader.getBiome(pos).getRegistryName(); + if (biomeMatcher.matches(b)) { + ResourceLocation d = reader.getWorld().getDimensionKey().getRegistryName(); + if (dimensionMatcher.matches(d)) { + return placeFeature(reader, chunkGenerator, pos); + } + } + } + return false; + } + + private boolean placeFeature(ISeedReader reader, ChunkGenerator chunkGenerator, BlockPos pos) { + return true; + } + +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index e4d85b4..5088863 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -13,13 +13,14 @@ import net.minecraft.block.pattern.BlockMatcher; import net.minecraft.util.WeightedList; import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.gen.feature.template.RuleTest; import org.lwjgl.system.Pointer; public class DefaultFeatureConfig implements IFeatureConfig { public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { return codec.group( - Codec.STRING.fieldOf("feature").forGetter((config) -> config.feature), - BlockMatcherConfig.CODEC.fieldOf("replaces").forGetter((config) -> config.replacer), + Codec.STRING.fieldOf("name").forGetter((config) -> config.feature), + RuleTest.CODEC.fieldOf("replaces").forGetter((config) -> config.replacer), DefaultFeatureParametersConfig.CODEC.fieldOf("parameters").forGetter((config) -> config.parameters), BiomeMatcherConfig.CODEC.fieldOf("biomes").forGetter((config) -> config.biomeMatch), DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch), @@ -32,9 +33,9 @@ public class DefaultFeatureConfig implements IFeatureConfig { public final DimensionMatcherConfig dimensionMatch; public final DefaultFeatureParametersConfig parameters; public final String feature; - public final BlockMatcherConfig replacer; + public final RuleTest replacer; - public DefaultFeatureConfig(String featureName, BlockMatcherConfig replacement, + public DefaultFeatureConfig(String featureName, RuleTest replacement, DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, DimensionMatcherConfig dimensions, WeightedList blocks ) { this.replacer = replacement; diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java new file mode 100644 index 0000000..d528503 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java @@ -0,0 +1,25 @@ +package com.mcmoddev.orespawn.world.gen.configs; + +import com.mcmoddev.orespawn.OreSpawn; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ISeedReader; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.IFeatureConfig; + +import javax.annotation.Nonnull; +import java.util.Random; + +public class OreSpawnFeature> extends ConfiguredFeature { + + public OreSpawnFeature(FC config, F feature) { + super(feature, config); + } + + @Override + public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos) { + OreSpawn.LOGGER.fatal("Inconceivably reached lowest level generator setup"); + return false; + } +} From 241b4eb2e7f4165707c74090ffa48cb183a95a8b Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 00:50:46 -0500 Subject: [PATCH 23/37] Its getting there... Can actually get into a world ! --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 15 +++--- .../utils/codecs/AllowDenyListBase.java | 9 ++-- .../utils/codecs/BiomeMatcherConfig.java | 10 +++- .../utils/codecs/BlockMatcherConfig.java | 27 ---------- .../utils/codecs/DimensionMatcherConfig.java | 6 +++ .../world/features/DefaultFeature.java | 50 +++++++++++++++++++ .../orespawn/world/features/Features.java | 9 ++++ .../world/gen/configs/DefaultFeature.java | 44 ---------------- .../gen/configs/DefaultFeatureConfig.java | 10 ++-- .../world/gen/configs/OreSpawnFeature.java | 11 ++-- .../configured_feature/test_entry.json | 20 ++++++++ 11 files changed, 116 insertions(+), 95 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java create mode 100644 src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java create mode 100644 src/main/java/com/mcmoddev/orespawn/world/features/Features.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java create mode 100644 src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 966b2d5..e8ccc06 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,14 +1,11 @@ package com.mcmoddev.orespawn; -import net.minecraft.item.Item; +import com.mcmoddev.orespawn.world.features.Features; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; -import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; -import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; @@ -22,6 +19,7 @@ public class OreSpawn { public OreSpawn() { // 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 @@ -33,13 +31,15 @@ public OreSpawn() { // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.addListener(this::itemRegistryEvent); MinecraftForge.EVENT_BUS.addListener(this::doServerStartTasks); + */ MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { + event.enqueueWork( () -> Registry.register(Registry.FEATURE, new ResourceLocation("orespawn4", "default"), Features.DEFAULT) ); } - + /* private void doClientStuff(final FMLClientSetupEvent event) { } @@ -55,4 +55,5 @@ private void itemRegistryEvent(final RegistryEvent.Register ev) { private void doServerStartTasks(final FMLServerStartingEvent ev) { } + */ } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java index 0a33953..27d450a 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java @@ -2,6 +2,7 @@ import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; + import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -11,10 +12,10 @@ public class AllowDenyListBase { private final ResourceLocation type; private final T baseKey; - private static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); - private static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); - private static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); - private static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); + public static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); + public static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); + public static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); + public static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); protected AllowDenyListBase(final ResourceLocation matchType, final T key, List baseList) { this.type = matchType; diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java index ab7d00a..2d71b1d 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java @@ -2,12 +2,15 @@ import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.data.BiomeMatcher; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; -import net.minecraft.world.gen.feature.OreFeature; +import net.minecraft.util.registry.Registry; +import java.util.Collections; import java.util.List; public class BiomeMatcherConfig { @@ -27,4 +30,9 @@ public BiomeMatcherConfig(final List entryList, final Resource this.data = ImmutableList.copyOf(entryList); this.type = type; } + + public BiomeMatcherConfig() { + type = AllowDenyListBase.denyall; + data = Collections.emptyList(); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java deleted file mode 100644 index 2afed0a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockMatcherConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import com.mcmoddev.orespawn.data.BlockType; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -public class BlockMatcherConfig { - public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(BlockTypeConfig.CODEC.listOf().fieldOf("blocks").forGetter((config) -> config.asTypeConfig())).apply(base, BlockMatcherConfig::new); - }); - - public final List blocks; - - public BlockMatcherConfig(final List blocksIn) { - this.blocks = new LinkedList<>(); - - this.blocks.addAll(blocksIn.stream().map(BlockType::new).collect(Collectors.toList())); - } - - public List asTypeConfig() { - return this.blocks.stream().map( bt -> new BlockTypeConfig(bt.getType(), bt.getName())).collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java index d36db9b..e6cabe9 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java @@ -5,6 +5,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.util.ResourceLocation; +import java.util.Collections; import java.util.List; public class DimensionMatcherConfig { @@ -24,4 +25,9 @@ public DimensionMatcherConfig(final List entryList, final Reso this.data = ImmutableList.copyOf(entryList); this.type = type; } + + public DimensionMatcherConfig() { + type = AllowDenyListBase.denyall; + data = Collections.emptyList(); + } } diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java new file mode 100644 index 0000000..47dff85 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java @@ -0,0 +1,50 @@ +package com.mcmoddev.orespawn.world.features; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.BiomeMatcher; +import com.mcmoddev.orespawn.data.DimensionMatcher; +import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; +import com.mcmoddev.orespawn.world.gen.configs.OreSpawnFeature; +import com.mojang.serialization.Codec; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ISeedReader; +import net.minecraft.world.World; +import net.minecraft.world.gen.ChunkGenerator; + +import javax.annotation.Nonnull; +import java.util.Random; + +public class DefaultFeature extends OreSpawnFeature { + private BiomeMatcher biomeMatcher; + private DimensionMatcher dimensionMatcher; + + public DefaultFeature(Codec codec) { + super(codec); + } + + @Override + public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { + if (biomeMatcher == null) biomeMatcher = new BiomeMatcher(config.biomeMatch); + if (dimensionMatcher == null) dimensionMatcher = new DimensionMatcher(config.dimensionMatch); + if (config.parameters.frequency <= rand.nextFloat()) { + ResourceLocation b = reader.getBiome(pos).getRegistryName(); + if (biomeMatcher.matches(b)) { + RegistryKey dx = reader.getWorld().getDimensionKey();//.getRegistryName(); + ResourceLocation d = dx.getLocation(); + + if (dimensionMatcher.matches(d)) { + return placeFeature(reader, chunkGenerator, pos, config); + } + } + } + return false; + } + + private boolean placeFeature(ISeedReader reader, ChunkGenerator chunkGenerator, BlockPos pos, DefaultFeatureConfig config) { + OreSpawn.LOGGER.info("placeFeature(%s, %s, %s) for %s", reader, chunkGenerator, pos, config.feature); + return true; + } + +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/Features.java b/src/main/java/com/mcmoddev/orespawn/world/features/Features.java new file mode 100644 index 0000000..f71a20f --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/world/features/Features.java @@ -0,0 +1,9 @@ +package com.mcmoddev.orespawn.world.features; + +import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.IFeatureConfig; + +public class Features extends net.minecraftforge.registries.ForgeRegistryEntry> { + public static final DefaultFeature DEFAULT = new DefaultFeature(DefaultFeatureConfig.CODEC); +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java deleted file mode 100644 index 59d2960..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeature.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.mcmoddev.orespawn.world.gen.configs; - -import com.mcmoddev.orespawn.data.BiomeMatcher; -import com.mcmoddev.orespawn.data.DimensionMatcher; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ISeedReader; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.feature.Feature; - -import javax.annotation.Nonnull; -import java.util.Random; - -public class DefaultFeature extends OreSpawnFeature { - private final DefaultFeatureConfig myConfig; - private final BiomeMatcher biomeMatcher; - private final DimensionMatcher dimensionMatcher; - - public DefaultFeature(DefaultFeatureConfig config) { - super(config, Feature.ORE); - myConfig = config; - biomeMatcher = new BiomeMatcher(config.biomeMatch); - dimensionMatcher = new DimensionMatcher(config.dimensionMatch); - } - - @Override - public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos) { - if (myConfig.parameters.frequency <= rand.nextFloat()) { - ResourceLocation b = reader.getBiome(pos).getRegistryName(); - if (biomeMatcher.matches(b)) { - ResourceLocation d = reader.getWorld().getDimensionKey().getRegistryName(); - if (dimensionMatcher.matches(d)) { - return placeFeature(reader, chunkGenerator, pos); - } - } - } - return false; - } - - private boolean placeFeature(ISeedReader reader, ChunkGenerator chunkGenerator, BlockPos pos) { - return true; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index 5088863..b5c28f6 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -1,20 +1,18 @@ package com.mcmoddev.orespawn.world.gen.configs; -import com.mcmoddev.orespawn.data.BiomeMatcher; -import com.mcmoddev.orespawn.data.DefaultFeatureParameters; -import com.mcmoddev.orespawn.data.DimensionMatcher; import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; -import com.mcmoddev.orespawn.utils.codecs.BlockMatcherConfig; import com.mcmoddev.orespawn.utils.codecs.DefaultFeatureParametersConfig; import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.BlockState; -import net.minecraft.block.pattern.BlockMatcher; import net.minecraft.util.WeightedList; import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.gen.feature.template.IRuleTestType; import net.minecraft.world.gen.feature.template.RuleTest; -import org.lwjgl.system.Pointer; + +import java.util.Collections; +import java.util.Random; public class DefaultFeatureConfig implements IFeatureConfig { public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java index d528503..49ca0ff 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java @@ -1,24 +1,23 @@ package com.mcmoddev.orespawn.world.gen.configs; import com.mcmoddev.orespawn.OreSpawn; +import com.mojang.serialization.Codec; import net.minecraft.util.math.BlockPos; import net.minecraft.world.ISeedReader; import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.Feature; import net.minecraft.world.gen.feature.IFeatureConfig; import javax.annotation.Nonnull; import java.util.Random; -public class OreSpawnFeature> extends ConfiguredFeature { - - public OreSpawnFeature(FC config, F feature) { - super(feature, config); +public class OreSpawnFeature extends Feature { + public OreSpawnFeature(Codec configCodec) { + super(configCodec); } @Override - public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos) { + public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator generator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull FC config) { OreSpawn.LOGGER.fatal("Inconceivably reached lowest level generator setup"); return false; } diff --git a/src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json b/src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json new file mode 100644 index 0000000..1a2320c --- /dev/null +++ b/src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json @@ -0,0 +1,20 @@ +{ + "type": "orespawn4:default", + "config": { + "name": "blargh", + "biomes": { "type": "orespawn4:allowall", "data": [] }, + "dimensions": { "type": "orespawn4:denylist", "data": [ "minecraft:the_end", "minecraft:the_nether" ] }, + "parameters": { + "maxHeight": 0xFFFFFFFF, + "minHeight": 0xFFFFFFFF, + "size": 32, + "variation": 16, + "frequency": 0.50 + }, + "replaces": { + "predicate_type": "minecraft:tag_match", + "tag": "minecraft:base_stone_overworld" + }, + "blocks": [ { "value": { "Name": "minecraft:coal_ore"}, "weight": 100 }, { "value": { "Name": "minecraft:iron_ore" }, "weight": 25 } ] + } +} From 33caf78c06f8e65649f326e03db0f78ea630c8b6 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 01:10:05 -0500 Subject: [PATCH 24/37] ...can't seem to get this working :( --- src/main/java/com/mcmoddev/orespawn/OreSpawn.java | 9 +++++++-- .../mcmoddev/orespawn/world/features/DefaultFeature.java | 1 + .../com/mcmoddev/orespawn/world/features/Features.java | 2 ++ src/main/resources/META-INF/mods.toml | 4 ++-- .../worldgen/configured_feature/test_entry.json | 0 src/main/resources/pack.mcmeta | 2 +- 6 files changed, 13 insertions(+), 5 deletions(-) rename src/main/resources/data/{orespawn => orespawn4}/worldgen/configured_feature/test_entry.json (100%) diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index e8ccc06..ab9292a 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -3,7 +3,9 @@ import com.mcmoddev.orespawn.world.features.Features; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.Registry; +import net.minecraft.world.gen.feature.Feature; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -11,7 +13,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@Mod("orespawn") +@Mod("orespawn4") public class OreSpawn { // Directly reference a log4j logger. public static final Logger LOGGER = LogManager.getLogger(); @@ -35,8 +37,11 @@ public OreSpawn() { MinecraftForge.EVENT_BUS.register(this); } + private void featureRegistry(final RegistryEvent.Register> event) { + event.getRegistry().register(Features.DEFAULT.setRegistryName(new ResourceLocation("orespawn4", "default"))); + } private void setup(final FMLCommonSetupEvent event) { - event.enqueueWork( () -> Registry.register(Registry.FEATURE, new ResourceLocation("orespawn4", "default"), Features.DEFAULT) ); +// event.enqueueWork( () -> Registry.register(Registry.FEATURE, new ResourceLocation("orespawn4", "default"), Features.DEFAULT) ); } /* diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java index 47dff85..fc7ad41 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java +++ b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java @@ -26,6 +26,7 @@ public DefaultFeature(Codec codec) { @Override public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { + OreSpawn.LOGGER.info("generate( ... ) for %s", config.feature); if (biomeMatcher == null) biomeMatcher = new BiomeMatcher(config.biomeMatch); if (dimensionMatcher == null) dimensionMatcher = new DimensionMatcher(config.dimensionMatch); if (config.parameters.frequency <= rand.nextFloat()) { diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/Features.java b/src/main/java/com/mcmoddev/orespawn/world/features/Features.java index f71a20f..88e68d6 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/features/Features.java +++ b/src/main/java/com/mcmoddev/orespawn/world/features/Features.java @@ -1,6 +1,8 @@ package com.mcmoddev.orespawn.world.features; import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; import net.minecraft.world.gen.feature.Feature; import net.minecraft.world.gen.feature.IFeatureConfig; diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index aa7c04a..f8ebc59 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -4,7 +4,7 @@ issueTrackerURL="https://github.com/MinecraftModDevelopmentMods/OreSpawn/issues/ # GNU LGPLv2.1 license="GNU LGPLv2.1" [[mods]] #mandatory -modId="orespawn" #mandatory +modId="orespawn4" #mandatory version="${file.jarVersion}" #mandatory displayName="MMD OreSpawn" #mandatory updateJSONURL="" # blank for now! @@ -28,4 +28,4 @@ A Mod for generating ore deposits in the world. Does not contain dragon girlfrie mandatory=true versionRange="[1.16,)" ordering="NONE" - side="BOTH" \ No newline at end of file + side="BOTH" diff --git a/src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json b/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json similarity index 100% rename from src/main/resources/data/orespawn/worldgen/configured_feature/test_entry.json rename to src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 0bd71cb..fe81797 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { - "description": "examplemod resources", + "description": "OreSpawn4 Resources and Data", "pack_format": 4, "_comment": "A pack_format of 4 requires json lang files. Note: we require v4 pack meta for all mods." } From be98eed232d9cc4e1bfd14ba7d3afc6af09c9a02 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 01:37:49 -0500 Subject: [PATCH 25/37] Seems to work, but the feature itself never seems to get used... Do I need to add another event to registered configured features ? --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 46 ++++++------------- .../configured_feature/test_entry.json | 6 +-- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index ab9292a..d1eb103 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -6,6 +6,8 @@ import net.minecraft.world.gen.feature.Feature; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -21,44 +23,22 @@ public class OreSpawn { public OreSpawn() { // 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 the doClientStuff method for modloading - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); - // Register the doClientStuff method for modloading - - // Register ourselves for server and other game events we are interested in - MinecraftForge.EVENT_BUS.addListener(this::itemRegistryEvent); - MinecraftForge.EVENT_BUS.addListener(this::doServerStartTasks); - */ - MinecraftForge.EVENT_BUS.register(this); } - private void featureRegistry(final RegistryEvent.Register> event) { - event.getRegistry().register(Features.DEFAULT.setRegistryName(new ResourceLocation("orespawn4", "default"))); - } + private void setup(final FMLCommonSetupEvent event) { // event.enqueueWork( () -> Registry.register(Registry.FEATURE, new ResourceLocation("orespawn4", "default"), Features.DEFAULT) ); } - /* - private void doClientStuff(final FMLClientSetupEvent event) { - } - - private void enqueueIMC(final InterModEnqueueEvent event) { - } - - private void processIMC(final InterModProcessEvent event) { - } - - private void itemRegistryEvent(final RegistryEvent.Register ev) { - - } - - private void doServerStartTasks(final FMLServerStartingEvent ev) { + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class RegistryEvents { + @SubscribeEvent + /** + * + */ + public static void featureRegistryEvent(final RegistryEvent.Register> event) { + event.getRegistry().register(Features.DEFAULT.setRegistryName("default")); + LOGGER.info("Registered %s", Features.DEFAULT.getRegistryName()); + } } - */ } diff --git a/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json b/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json index 1a2320c..96b1480 100644 --- a/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json +++ b/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json @@ -5,8 +5,8 @@ "biomes": { "type": "orespawn4:allowall", "data": [] }, "dimensions": { "type": "orespawn4:denylist", "data": [ "minecraft:the_end", "minecraft:the_nether" ] }, "parameters": { - "maxHeight": 0xFFFFFFFF, - "minHeight": 0xFFFFFFFF, + "maxHeight": 255, + "minHeight": 0, "size": 32, "variation": 16, "frequency": 0.50 @@ -15,6 +15,6 @@ "predicate_type": "minecraft:tag_match", "tag": "minecraft:base_stone_overworld" }, - "blocks": [ { "value": { "Name": "minecraft:coal_ore"}, "weight": 100 }, { "value": { "Name": "minecraft:iron_ore" }, "weight": 25 } ] + "blocks": [ { "data": { "Name": "minecraft:coal_ore" }, "weight": 100 }, { "data": { "Name": "minecraft:iron_ore" }, "weight": 25 } ] } } From 9c5fbb7cb96e50410a99b674153c9b8a92fdde84 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 02:28:17 -0500 Subject: [PATCH 26/37] Lots of tiny changes, nothing major fixed from the last commit. Almost entirely just added debugging and a few pokes to see if I could get things working by throwing shit at the wall while dead tired. But I know what I need to do now and it is rather brain-dead simple. I need to add some tracking of configured features so they can actually be registered in the correct registry - or just do it on the fly... Vanilla and all existing mods appear to do the WorldGenRegistry stuff hard-coded for at least one feature... this isn't what I want here, so I'm going to have to do it separately and cross my fingers. Thats for later, though. --- src/main/java/com/mcmoddev/orespawn/OreSpawn.java | 5 ++++- .../mcmoddev/orespawn/world/features/DefaultFeature.java | 5 +++++ .../orespawn/world/gen/configs/DefaultFeatureConfig.java | 9 +++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index d1eb103..5cdb721 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -3,6 +3,7 @@ import com.mcmoddev.orespawn.world.features.Features; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.Registry; +import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.Feature; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; @@ -18,7 +19,7 @@ @Mod("orespawn4") public class OreSpawn { // Directly reference a log4j logger. - public static final Logger LOGGER = LogManager.getLogger(); + public static final Logger LOGGER = LogManager.getFormatterLogger(); public OreSpawn() { // Register the setup method for modloading @@ -39,6 +40,8 @@ public static class RegistryEvents { public static void featureRegistryEvent(final RegistryEvent.Register> event) { event.getRegistry().register(Features.DEFAULT.setRegistryName("default")); LOGGER.info("Registered %s", Features.DEFAULT.getRegistryName()); + + event.getRegistry().getEntries().stream().forEach( ent -> LOGGER.fatal( "feature %s of type %s", ent.getValue().getRegistryName(), ent.getValue().getCodec())); } } } diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java index fc7ad41..9b59a1c 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java +++ b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java @@ -24,6 +24,11 @@ public DefaultFeature(Codec codec) { super(codec); } + public boolean place(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { + OreSpawn.LOGGER.info("Yes, the method needed is called place and not generate, regardless of what IDEA says"); + return this.generate(reader, chunkGenerator, rand, pos, config); + } + @Override public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { OreSpawn.LOGGER.info("generate( ... ) for %s", config.feature); diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index b5c28f6..ca2f9f5 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -1,18 +1,26 @@ package com.mcmoddev.orespawn.world.gen.configs; +import com.mcmoddev.orespawn.OreSpawn; import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; import com.mcmoddev.orespawn.utils.codecs.DefaultFeatureParametersConfig; import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.BlockState; +import net.minecraft.util.RegistryKey; import net.minecraft.util.WeightedList; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.util.registry.MutableRegistry; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.IFeatureConfig; import net.minecraft.world.gen.feature.template.IRuleTestType; import net.minecraft.world.gen.feature.template.RuleTest; import java.util.Collections; +import java.util.Optional; import java.util.Random; +import java.util.stream.Stream; public class DefaultFeatureConfig implements IFeatureConfig { public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { @@ -42,5 +50,6 @@ public DefaultFeatureConfig(String featureName, RuleTest replacement, this.dimensionMatch = dimensions; this.parameters = parameters; this.feature = featureName; + OreSpawn.LOGGER.info("Feature %s configured", featureName); } } From 782b954730e6a5b247487896dd4989a5fbddf77e Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 11:36:01 -0500 Subject: [PATCH 27/37] still not working right, but here is what I thought would make this work last night... --- .../world/gen/configs/DefaultFeatureConfig.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index ca2f9f5..c12dbb6 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -4,6 +4,8 @@ import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; import com.mcmoddev.orespawn.utils.codecs.DefaultFeatureParametersConfig; import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; +import com.mcmoddev.orespawn.world.features.DefaultFeature; +import com.mcmoddev.orespawn.world.features.Features; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.block.BlockState; @@ -12,6 +14,7 @@ import net.minecraft.util.registry.DynamicRegistries; import net.minecraft.util.registry.MutableRegistry; import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.WorldGenRegistries; import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.IFeatureConfig; import net.minecraft.world.gen.feature.template.IRuleTestType; @@ -31,7 +34,7 @@ public class DefaultFeatureConfig implements IFeatureConfig { BiomeMatcherConfig.CODEC.fieldOf("biomes").forGetter((config) -> config.biomeMatch), DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch), WeightedList.getCodec(BlockState.CODEC).fieldOf("blocks").forGetter((config) -> config.blocks) - ).apply(codec, DefaultFeatureConfig::new); + ).apply(codec, DefaultFeatureConfig::generateFeature); }); public final WeightedList blocks; @@ -41,6 +44,15 @@ public class DefaultFeatureConfig implements IFeatureConfig { public final String feature; public final RuleTest replacer; + private static DefaultFeatureConfig generateFeature(String featureName, RuleTest replacement, + DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, + DimensionMatcherConfig dimensions, WeightedList blocks ) { + DefaultFeatureConfig z = new DefaultFeatureConfig(featureName, replacement, parameters, biomes, dimensions, blocks); + Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, featureName, Features.DEFAULT.withConfiguration(z)); + WorldGenRegistries.CONFIGURED_FEATURE.getEntries().stream().forEach( ent -> OreSpawn.LOGGER.info( "%s --> %s", ent.getValue().getFeature().getRegistryName(), ent.getValue().getConfig())); + return z; + } + public DefaultFeatureConfig(String featureName, RuleTest replacement, DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, DimensionMatcherConfig dimensions, WeightedList blocks ) { From dab50e32dfd0a96869773e8846feddded38de8ce Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 12:12:38 -0500 Subject: [PATCH 28/37] more poking, things still are not working... --- .../com/mcmoddev/orespawn/utils/Helpers.java | 19 +------------------ .../gen/configs/DefaultFeatureConfig.java | 4 ++-- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java index c335ce7..ad6d2a3 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java @@ -36,7 +36,7 @@ private static Pair getNameBits(final String defaultDomain, fina return Pair.of(domain, path); } public static ResourceLocation makeInternalResourceLocation(final String locIn) { - Pair bits = getNameBits("orespawn", locIn); + Pair bits = getNameBits("orespawn4", locIn); return makeResourceLocationFlat(bits.getLeft(), bits.getRight()); } @@ -56,21 +56,4 @@ public static ResourceLocation makeBlockResourceLocation(final String blockName) } return result; } - public static Path makePath(ResourceLocation rl, String type) { - Path result; - switch (type) { - case Constants.FileBits.DISK: - result = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, rl.getPath() + ".json"); - break; - case Constants.FileBits.RESOURCE: - ModFile mf = ModList.get().getModFileById(rl.getNamespace()).getFile(); - result = mf.getLocator().findPath(mf, "assets", "orespawn4-data", rl.getPath() + ".json"); - break; - default: - OreSpawn.LOGGER.error("Asked to resolve a path for {} of type {} -- I do not know how to do this", rl.toString(), type); - result = Paths.get(Constants.FileBits.CONFIG_DIR, Constants.FileBits.OS4, "error.json"); - break; - } - return result; - } } diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index c12dbb6..5b1c92e 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -48,8 +48,8 @@ private static DefaultFeatureConfig generateFeature(String featureName, RuleTest DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, DimensionMatcherConfig dimensions, WeightedList blocks ) { DefaultFeatureConfig z = new DefaultFeatureConfig(featureName, replacement, parameters, biomes, dimensions, blocks); - Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, featureName, Features.DEFAULT.withConfiguration(z)); - WorldGenRegistries.CONFIGURED_FEATURE.getEntries().stream().forEach( ent -> OreSpawn.LOGGER.info( "%s --> %s", ent.getValue().getFeature().getRegistryName(), ent.getValue().getConfig())); + Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, String.format("orespawn4:%s",featureName), Features.DEFAULT.withConfiguration(z)); + WorldGenRegistries.CONFIGURED_FEATURE.getEntries().stream().forEach( ent -> OreSpawn.LOGGER.info( "%s --> %s [%s]", ent.getKey().getRegistryName(), ent.getValue().getConfig(), ent.getValue().getFeature().getRegistryName())); return z; } From 32d4775595116dedc534b276df0d35f1713a64fc Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 17:42:06 -0500 Subject: [PATCH 29/37] fighting with things to try and make this work... fingers crossed ? --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 31 ++++++++++++++----- .../orespawn/utils/mixins/BiomeAccessor.java | 12 +++++++ .../BiomeGenerationSettingsAccessor.java | 17 ++++++++++ .../orespawn/utils/mixins/ServerAccessor.java | 12 +++++++ .../gen/configs/DefaultFeatureConfig.java | 20 +++++++++--- src/main/resources/orespawn.mixins.json | 14 +++++++++ 6 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java create mode 100644 src/main/resources/orespawn.mixins.json diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 5cdb721..76424f5 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,8 +1,8 @@ package com.mcmoddev.orespawn; -import com.mcmoddev.orespawn.world.features.Features; -import net.minecraft.util.ResourceLocation; +import com.mcmoddev.orespawn.utils.mixins.BiomeGenerationSettingsAccessor; import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.BiomeGenerationSettings; import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.Feature; import net.minecraftforge.common.MinecraftForge; @@ -11,11 +11,19 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; + +import com.mcmoddev.orespawn.world.features.Features; +import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; + @Mod("orespawn4") public class OreSpawn { // Directly reference a log4j logger. @@ -24,24 +32,31 @@ public class OreSpawn { public OreSpawn() { // Register the setup method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::start); +// MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::finalizeFeatureRegistration); } + public void start(final FMLServerAboutToStartEvent event) { + event.getServer().getDynamicRegistries().getRegistry(Registry.BIOME_KEY).stream().forEach(b -> { + BiomeGenerationSettings settings = b.getGenerationSettings(); + List>>> data = new LinkedList<>(); + data.addAll(settings.getFeatures()); + List>> cc = new LinkedList<>(); + DefaultFeatureConfig.getMyFeatures().values().stream().forEach( cf -> cc.add(() -> cf)); + data.add(cc); + settings.setFeatures(data); + }); + } private void setup(final FMLCommonSetupEvent event) { -// event.enqueueWork( () -> Registry.register(Registry.FEATURE, new ResourceLocation("orespawn4", "default"), Features.DEFAULT) ); } @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { @SubscribeEvent - /** - * - */ public static void featureRegistryEvent(final RegistryEvent.Register> event) { event.getRegistry().register(Features.DEFAULT.setRegistryName("default")); LOGGER.info("Registered %s", Features.DEFAULT.getRegistryName()); - - event.getRegistry().getEntries().stream().forEach( ent -> LOGGER.fatal( "feature %s of type %s", ent.getValue().getRegistryName(), ent.getValue().getCodec())); } } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java new file mode 100644 index 0000000..0bb7a76 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java @@ -0,0 +1,12 @@ +package com.mcmoddev.orespawn.utils.mixins; + +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeGenerationSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor + BiomeGenerationSettings getGenerationSettings(); +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java new file mode 100644 index 0000000..f43324b --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java @@ -0,0 +1,17 @@ +package com.mcmoddev.orespawn.utils.mixins; + +import net.minecraft.world.biome.BiomeGenerationSettings; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; +import java.util.function.Supplier; + +@Mixin(BiomeGenerationSettings.class) +public interface BiomeGenerationSettingsAccessor { + @Accessor + @Mutable + void setFeatures(List>>> features); +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java new file mode 100644 index 0000000..6bb85ea --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java @@ -0,0 +1,12 @@ +package com.mcmoddev.orespawn.utils.mixins; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.registry.DynamicRegistries; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MinecraftServer.class) +public interface ServerAccessor { + @Accessor + DynamicRegistries.Impl getDynamicRegistries(); +} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java index 5b1c92e..dc3722a 100644 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java @@ -20,9 +20,7 @@ import net.minecraft.world.gen.feature.template.IRuleTestType; import net.minecraft.world.gen.feature.template.RuleTest; -import java.util.Collections; -import java.util.Optional; -import java.util.Random; +import java.util.*; import java.util.stream.Stream; public class DefaultFeatureConfig implements IFeatureConfig { @@ -44,12 +42,16 @@ public class DefaultFeatureConfig implements IFeatureConfig { public final String feature; public final RuleTest replacer; + private static final Map> myFeatures = new TreeMap<>(); + private static DefaultFeatureConfig generateFeature(String featureName, RuleTest replacement, DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, DimensionMatcherConfig dimensions, WeightedList blocks ) { DefaultFeatureConfig z = new DefaultFeatureConfig(featureName, replacement, parameters, biomes, dimensions, blocks); - Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, String.format("orespawn4:%s",featureName), Features.DEFAULT.withConfiguration(z)); - WorldGenRegistries.CONFIGURED_FEATURE.getEntries().stream().forEach( ent -> OreSpawn.LOGGER.info( "%s --> %s [%s]", ent.getKey().getRegistryName(), ent.getValue().getConfig(), ent.getValue().getFeature().getRegistryName())); + if (!myFeatures.containsKey(featureName)) { + myFeatures.put(featureName, Features.DEFAULT.withConfiguration(z)); + Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, featureName, Features.DEFAULT.withConfiguration(z)); + } return z; } @@ -64,4 +66,12 @@ public DefaultFeatureConfig(String featureName, RuleTest replacement, this.feature = featureName; OreSpawn.LOGGER.info("Feature %s configured", featureName); } + + public Stream> getConfiguredFeatures() { + return myFeatures.values().stream(); + } + + public static Map> getMyFeatures() { + return Collections.unmodifiableMap(myFeatures); + } } diff --git a/src/main/resources/orespawn.mixins.json b/src/main/resources/orespawn.mixins.json new file mode 100644 index 0000000..1af48b0 --- /dev/null +++ b/src/main/resources/orespawn.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "com.mcmoddev.orespawn.utils.mixins", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "ServerAccessor", + "BiomeAccessor", + "BiomeGenerationSettingsAccessor" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8" +} From 5a04fe42b414485b2d1ebebd65115fd5698fa2b7 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 18:46:31 -0500 Subject: [PATCH 30/37] Almost functional... now why are mixins not applying ? --- build.gradle | 18 +++++++++++++++++- .../java/com/mcmoddev/orespawn/OreSpawn.java | 5 +++-- .../orespawn/utils/mixins/BiomeAccessor.java | 12 ------------ ...spawn.mixins.json => orespawn4.mixins.json} | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java rename src/main/resources/{orespawn.mixins.json => orespawn4.mixins.json} (87%) diff --git a/build.gradle b/build.gradle index 0b486d0..26c40e6 100644 --- a/build.gradle +++ b/build.gradle @@ -20,8 +20,10 @@ buildscript { name = 'sonatype' url = 'https://oss.sonatype.org/content/groups/public' } + maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { + classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT' if (GradleVersion.current() >= GradleVersion.version('5.0')) { classpath group: 'gradle.plugin.com.github.spotbugs.snom', name: 'spotbugs-gradle-plugin', version: '4.7.1' } else if (GradleVersion.current() >= GradleVersion.version('4.7')) { @@ -76,6 +78,10 @@ if (gradleNewerThan('4.0')) { } else { apply plugin: 'findbugs' } +apply plugin: 'org.spongepowered.mixin' +mixin { + add sourceSets.main, "orespawn4.refmap.json" +} //import net.minecraftforge.gradle.common.task.SignJar @@ -531,6 +537,7 @@ dependencies { if (gradleNewerThan('4.6')) { annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' } + annotationProcessor 'org.spongepowered:mixin:0.8:processor' } processResources { @@ -567,7 +574,8 @@ jar { 'Implementation-Vendor' : project.vendor, 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}" + 'Built-On': "${project.forge_mc_version}-${project.forge_version}", + 'MixinConfigs': "orespawn4.mixins.json" ) if (!isFG3()) { if (project.hasProperty('core_plugin')) { @@ -1204,3 +1212,11 @@ checkerFramework { ] } */ +jar { + manifest { + attributes([ + // Existing properties here + "MixinConfigs": "orespawn4.mixins.json" + ]) + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 76424f5..73dfdae 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,6 +1,7 @@ package com.mcmoddev.orespawn; import com.mcmoddev.orespawn.utils.mixins.BiomeGenerationSettingsAccessor; +import com.mcmoddev.orespawn.utils.mixins.ServerAccessor; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.BiomeGenerationSettings; import net.minecraft.world.gen.feature.ConfiguredFeature; @@ -37,14 +38,14 @@ public OreSpawn() { } public void start(final FMLServerAboutToStartEvent event) { - event.getServer().getDynamicRegistries().getRegistry(Registry.BIOME_KEY).stream().forEach(b -> { + ((ServerAccessor) event.getServer()).getDynamicRegistries().getRegistry(Registry.BIOME_KEY).stream().forEach(b -> { BiomeGenerationSettings settings = b.getGenerationSettings(); List>>> data = new LinkedList<>(); data.addAll(settings.getFeatures()); List>> cc = new LinkedList<>(); DefaultFeatureConfig.getMyFeatures().values().stream().forEach( cf -> cc.add(() -> cf)); data.add(cc); - settings.setFeatures(data); + ((BiomeGenerationSettingsAccessor) settings).setFeatures(data); }); } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java deleted file mode 100644 index 0bb7a76..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mcmoddev.orespawn.utils.mixins; - -import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.BiomeGenerationSettings; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(Biome.class) -public interface BiomeAccessor { - @Accessor - BiomeGenerationSettings getGenerationSettings(); -} diff --git a/src/main/resources/orespawn.mixins.json b/src/main/resources/orespawn4.mixins.json similarity index 87% rename from src/main/resources/orespawn.mixins.json rename to src/main/resources/orespawn4.mixins.json index 1af48b0..9894d8d 100644 --- a/src/main/resources/orespawn.mixins.json +++ b/src/main/resources/orespawn4.mixins.json @@ -2,9 +2,9 @@ "required": true, "package": "com.mcmoddev.orespawn.utils.mixins", "compatibilityLevel": "JAVA_8", + "refmap": "orespawn4.refmap.json", "mixins": [ "ServerAccessor", - "BiomeAccessor", "BiomeGenerationSettingsAccessor" ], "injectors": { From e53072b3898d3eee29d9ae15544a9844701b60a3 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 19:33:02 -0500 Subject: [PATCH 31/37] streamline the build.gradle to make things work with the mixin processor and such... --- build.gradle | 1205 +---------------- .../BiomeGenerationSettingsAccessor.java | 1 + 2 files changed, 43 insertions(+), 1163 deletions(-) diff --git a/build.gradle b/build.gradle index 26c40e6..367d21d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,263 +1,32 @@ buildscript { repositories { - mavenLocal() + // These repositories are only for Gradle plugins, put any other repositories in the repository block further below + maven { url = 'https://maven.minecraftforge.net' } mavenCentral() - maven { - name = 'jcenter' - url = 'https://jcenter.bintray.com/' - } - removeIf { it instanceof MavenArtifactRepository && it.url.host == 'files.minecraftforge.net' } - removeIf { it instanceof MavenArtifactRepository && it.url.host == 'maven.minecraftforge.net' } - maven { - name = 'forge' - url = 'https://maven.minecraftforge.net/' - } - maven { - name = 'gradle' - url 'https://plugins.gradle.org/m2/' - } - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public' - } maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT' - if (GradleVersion.current() >= GradleVersion.version('5.0')) { - classpath group: 'gradle.plugin.com.github.spotbugs.snom', name: 'spotbugs-gradle-plugin', version: '4.7.1' - } else if (GradleVersion.current() >= GradleVersion.version('4.7')) { - classpath group: 'gradle.plugin.com.github.spotbugs', name: 'spotbugs-gradle-plugin', version: '1.6.9' - } else if (GradleVersion.current() >= GradleVersion.version('4.0')) { - classpath group: 'gradle.plugin.com.github.spotbugs', name: 'spotbugs-gradle-plugin', version: '1.6.5' - } - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: project.fg_version, changing: true - if (GradleVersion.current() > GradleVersion.version('3.3')) { - classpath group: 'org.checkerframework', name: 'checkerframework-gradle-plugin', version: '0.5.22' -// } else { -// classpath group: 'com.jaredsburrows', name: 'gradle-checker-framework-plugin', version: '0.2.1' - } } } plugins { -//id 'com.gradle.build-scan' version '1.16' // Gradle 4.x or earlier -//id 'com.gradle.build-scan' version '3.6.3' // Gradle 5.x -//id 'com.jaredsburrows.checkerframework' version '0.2.1'// Gradle 3.3 or earlier -//id 'com.jaredsburrows.checkerframework' version '0.2.2'// Gradle 3.4/4.0 or later -//id 'com.github.spotbugs' version '4.7.1' // 1.6.5 for Gradle 4.0-4.6, 1.6.9 for Gradle 4.7+ - id 'com.matthewprenger.cursegradle' version '1.4.0' - id 'org.sonarqube' version '3.3' id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.66' -//id 'org.junit.platform.gradle.plugin' version '1.2.0' // Gradle 3.5 through 4.5 - id 'jacoco' - id 'checkstyle' - id 'pmd' id 'eclipse' id 'idea' id 'maven-publish' } - -if (isFG1()) { - apply plugin: 'forge' -} else if (isFG2()) { - apply plugin: 'net.minecraftforge.gradle.forge' -} else if (isFG3()) { - apply plugin: 'net.minecraftforge.gradle' -} - -import org.gradle.util.GradleVersion -if (gradleNewerThan('3.4')) { -// apply plugin: 'org.checkerframework' -} else { -// apply plugin: 'com.jaredsburrows.checkerframework' -} - -if (gradleNewerThan('4.0')) { - apply plugin: 'com.github.spotbugs' -} else { - apply plugin: 'findbugs' -} +apply plugin: 'net.minecraftforge.gradle' apply plugin: 'org.spongepowered.mixin' mixin { add sourceSets.main, "orespawn4.refmap.json" } -//import net.minecraftforge.gradle.common.task.SignJar - -gradleEnterprise { - buildScan { - if (System.getenv('CI')) { - publishAlways() - } -// publishAlwaysIf(System.getenv('CI')) - termsOfServiceUrl = 'https://gradle.com/terms-of-service' - termsOfServiceAgree = 'yes' - } -} - -project.afterEvaluate { - //final def junitPlatformTestTask = project.tasks.getByName('junitPlatformTest') - - // configure jacoco to analyze the junitPlatformTest task - jacoco { - // this tool version is compatible with - toolVersion = '0.8.3' // 0.8.3 uses ASM 7.0.4, 0.8.4 uses ASM 7.1, 0.8.6 uses ASM 8.0.1, 0.8.7 uses ASM 9.1 - // Not needed with new way - //if (gradleOlderThan('4.6')) { - //applyTo junitPlatformTestTask - //} - } - - // create junit platform jacoco task - project.task(type: JacocoReport, 'junitPlatformJacocoReport', - { - if (gradleNewerThan('5.0')) { - getSourceDirectories().from(files('./src/main')) - getClassDirectories().from(files("${project.buildDir}/classes/main")) - } else { - sourceDirectories = files('./src/main') - classDirectories = files("${project.buildDir}/classes/main") - } - // Not needed with new way - //if (gradleOlderThan('4.6')) { - // executionData junitPlatformTestTask - //} - }) -} - -// Only works with JUnit Platform Launcher 1.2.0 and earlier -if (gradleOlderThan('4.6')) { - junitPlatform { - // platformVersion '1.2.0' - // logManager 'org.apache.logging.log4j.jul.LogManager' - reportsDir file("${project.buildDir}/test-results/junit-platform") - // enableStandardTestTask true - // selectors (optional) - // filters (optional) - } -} - -if (gradleNewerThan('4.0')) { - spotbugs { - ignoreFailures = true - toolVersion = '4.2.3' // 3.1.7 uses ASM 6.2, 3.1.8 uses ASM 6.2.1 - effort = 'max' - reportLevel = 'low' - //includeFilter = file("$rootProject.projectDir/config/spotbugs-filter.xml") - } - -//tasks.withType(com.github.spotbugs.SpotBugsTask) { // Gradle 4.x and earlier - tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { // Gradle 5.x and later - reports { - if (isCi()) { - xml.enabled = true - html.enabled = false - } else { - xml.enabled = false - html.enabled = true - } - } - } -} else { - findbugs { - toolVersion = "3.0.1" // Required for Java 8 -// toolVersion = "2.0.3" // Required for Java ? -// toolVersion = "1.3.9" // Required for Java ? - ignoreFailures = true - effort = 'max' - reportLevel = 'low' - } -} - -checkstyle { - ignoreFailures = true - if (gradleNewerThan('2.7')) { - toolVersion = '8.43' - } else { - configFile = file("${project.projectDir}/config/checkstyle/checkstyle-6.xml") - toolVersion = '6.7' // 6.19, 6.2 - ~6.19/8.28 requires jdk7, 6.0 - 6.1.1 jdk6, 5.9 for jdk5 - } - showViolations = false -} - -pmd { - if (gradleNewerThan('2.1')) { - consoleOutput = false - } - ignoreFailures = true - sourceSets = [sourceSets.main, sourceSets.test] - reportsDir = file("$project.buildDir/reports/pmd") - if (gradleNewerThan('6.8')) { - rulesMinimumPriority = 5 - } else if (gradleNewerThan('2.8')) { - rulePriority = 5 - } - ruleSets = [ ] - if (gradleNewerThan('5.6')) { - ruleSetFiles = files('config/pmd/pmd.xml') - toolVersion = '6.36.0' - incrementalAnalysis = true - } else if (gradleNewerThan('2.4')) { - ruleSetFiles = files('config/pmd/pmd-5.8.xml') - toolVersion = '5.8.1' - } else { - ruleSetFiles = files('config/pmd/pmd-5.8.xml') - toolVersion = '5.1.3' - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.compilerArgs = [ - '-Xlint:deprecation', // deprecation, rawtypes, cast, unchecked, all - //'-Xdiags:verbose', - //'-Werror' - ] - compileJava.options.compilerArgs += '-proc:none' -} - -tasks.withType(Test) { - compileTestJava.options.compilerArgs += '-proc:none' -} - -javadoc { - failOnError = false -} - -test { - if (gradleNewerThan('4.6')) { - useJUnitPlatform() - /* - useJUnitPlatform { - includeTags 'fast', 'smoke & feature-a' - //excludeTags 'slow', 'ci' - includeEngines 'junit-jupiter' - //excludeEngines 'junit-vintage' - } - */ - } -} - -if (project.hasProperty('mod_version')) { - project.ext.short_version = project.mod_version -} else if (project.hasProperty('mod_version_file')) { - ext.short_version = getModVersion((project.hasProperty('mod_version_const') ? project.mc_version : 'VERSION'), project.mod_version_file) -} else { - project.ext.short_version = getModVersion((project.hasProperty('mod_version_const') ? project.mc_version : 'VERSION')) -} +project.ext.short_version = '4.0.0' version = (project.hasProperty('mc_version') ? project.mc_version : project.forge_mc_version) + '-' + short_version project.ext.display_version = short_version -if ((System.getenv('BUILD_NUMBER')) || (System.getenv('TRAVIS_BUILD_NUMBER')) || (System.getenv('CIRCLE_BUILD_NUM'))) { - version += '.' + buildNumber - display_version += '.' + buildNumber -} - -static String getBuildNumber() { - return System.getenv('BUILD_NUMBER') ?: System.getenv('TRAVIS_BUILD_NUMBER') ?: System.getenv('CIRCLE_BUILD_NUM') ?: '0' -} - sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 compileJava.sourceCompatibility = JavaVersion.VERSION_1_8 @@ -270,953 +39,63 @@ eclipse.project { idea.module { downloadJavadoc = true - //inheritOutputDirs = true -} - -class Secrets { - Map m_data = null - - final String getProperty(final String key) { - return (m_data && (m_data[key] != null)) ? m_data[key] : '' - } -} - -final def secretFile -if (System.getenv('SECRET_FILE')) { - secretFile = file System.getenv().SECRET_FILE -} else { - secretFile = file 'secret.json' -} - -import groovy.json.JsonSlurper -project.ext.secret = new Secrets() -if (secretFile.exists()) { - secretFile.withReader { - project.secret.m_data = new JsonSlurper().parse it - } -} else { - project.secret.m_data = new JsonSlurper().parseText( - '{ "username" : "' + (System.getenv('userMaven') ?: '') + '",' - + '"password" : "' + (System.getenv('authMaven') ?: '') + '",' - + '"url" : "' + (System.getenv('urlMaven') ?: '') + '",' - + '"curseforgeAPIKey" : "' + (System.getenv('curse_auth') ?: '00000000-0000-0000-0000-000000000000') + '",' - + '"sonarHost" : "' + (System.getenv('SONAR_HOST_URL') ?: 'https://sonarcloud.io') + '",' - + '"sonarOrganization" : "' + (System.getenv('SONAR_ORGANIZATION') ?: '') + '",' - + '"sonarToken" : "' + (System.getenv('SONAR_TOKEN') ?: 0) + '"}' - ) } minecraft { - if (isFG3()) { - mappings channel: project.mcp_mappings_channel, version: project.mcp_mappings_version - } else { - version = project.forge_mc_version + '-' + project.forge_version - if ((project.forge_mc_version != "1.6.4") && (project.forge_mc_version != "1.7.2")) { - mappings = project.mcp_mappings_version - } + mappings channel: project.mcp_mappings_channel, version: project.mcp_mappings_version - if ((project.fg_version != '1.0-SNAPSHOT') && (project.fg_version != '1.1-SNAPSHOT')) { - if (project.hasProperty('runDir')) { - runDir = project.runDir - } else if (file('../run').exists()) { - runDir = '../run' - } else { - runDir = 'run' - } - } else { - if (project.hasProperty('runDir')) { - assetDir = project.runDir - } else if (file('../assets').exists()) { - assetDir = '../assets' - } else { - assetDir = 'eclipse/assets' - } - } + runs { + client { + workingDirectory project.file('run') - replace '@MOD_VERSION@', project.short_version + taskName 'runClient' + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' - if (project.hasProperty('signSHA1') && getBooleanProperty('do_sign_jar') && !isFG1()) { - replace '@FINGERPRINT@', project.findProperty('signSHA1') - } else { - replace '@FINGERPRINT@', '' - } - } + mods {} + } - if (project.hasProperty('core_plugin')) { - clientJvmArgs += "-Dfml.coreMods.load=${project.core_plugin}" - serverJvmArgs += "-Dfml.coreMods.load=${project.core_plugin}" - } + server { + workingDirectory project.file('run') - if (project.hasProperty('mc_username')) { - clientRunArgs += ['--username', project.mc_username] - if (project.hasProperty('mc_password')) { - clientRunArgs += ['--password', project.mc_password] - } - } - if (project.hasProperty('mc_uuid')) { - clientRunArgs += ['--uuid', project.mc_uuid] - } - - if (getBooleanProperty('mc_server_nogui')) { - serverRunArgs += 'nogui' - } - - if (getBooleanProperty('forge_do_not_backup')) { - serverJvmArgs += '-Dfml.doNotBackup=true' - clientJvmArgs += '-Dfml.doNotBackup=true' - } - - if (getBooleanProperty('forge_query_result_confirm')) { - serverJvmArgs += '-Dfml.queryResult=confirm' - } - - if (getBooleanProperty('log4j_skip_jansi')) { - serverJvmArgs += '-Dlog4j.skipJansi=true' - clientJvmArgs += '-Dlog4j.skipJansi=true' - } - - if (project.hasProperty('mc_resolution')) { - final def res = [ - '480p': [640, 480], - '576p': [640, 576], - '720p': [1280, 720], - '1080p': [1920, 1080], - 'custom': [project.findProperty('mc_custom_resolution_width'), project.findProperty('mc_custom_resolution_height')]] - final def choice = res?.get(project.mc_resolution) - if (choice != null) { - clientRunArgs += ['--width', choice[0]] - clientRunArgs += ['--height', choice[1]] - } - } - - if (project.hasProperty('client_jvm_args')) { - clientJvmArgs += project.client_jvm_args - } - if (project.hasProperty('server_jvm_args')) { - serverJvmArgs += project.server_jvm_args - } - - if (project.hasProperty('client_game_args')) { - clientRunArgs += project.client_game_args - } - if (project.hasProperty('server_game_args')) { - serverRunArgs += project.server_game_args - } - - if (isFG2()) { - makeObfSourceJar = getBooleanProperty('create_source_jar') - - useDepAts = getBooleanProperty('dep_has_ats') - } - - if (isFG3()) { - if (project.hasProperty('mod_at_file')) { - accessTransformer = file(findProperty('mod_at_file')) - } - - // Default run configurations. - // These can be tweaked, removed, or duplicated as needed. - runs { - client { - workingDirectory project.file('run') - - taskName 'runClient' - - // Recommended logging data for a userdev environment - // The markers can be changed as needed. - // "SCAN": For mods scan. - // "REGISTRIES": For firing of registry events. - // "REGISTRYDUMP": For getting the contents of all registries. - property 'forge.logging.markers', 'REGISTRIES' - - // Recommended logging level for the console - // You can set various levels here. - // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels - property 'forge.logging.console.level', 'debug' - - mods { - examplemod { - source sourceSets.main - } - } - } - - server { - workingDirectory project.file('run') + taskName 'runServer' + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' - taskName 'runServer' + } - // Recommended logging data for a userdev environment - // The markers can be changed as needed. - // "SCAN": For mods scan. - // "REGISTRIES": For firing of registry events. - // "REGISTRYDUMP": For getting the contents of all registries. - property 'forge.logging.markers', 'REGISTRIES' + data { + workingDirectory project.file('run') - // Recommended logging level for the console - // You can set various levels here. - // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels - property 'forge.logging.console.level', 'debug' - - mods { - examplemod { - source sourceSets.main - } - } - } - - data { - workingDirectory project.file('run') - - // Recommended logging data for a userdev environment - // The markers can be changed as needed. - // "SCAN": For mods scan. - // "REGISTRIES": For firing of registry events. - // "REGISTRYDUMP": For getting the contents of all registries. - property 'forge.logging.markers', 'REGISTRIES' - - // Recommended logging level for the console - // You can set various levels here. - // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels - property 'forge.logging.console.level', 'debug' - - // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. - args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') - - mods { - examplemod { - source sourceSets.main - } - } - } - } - } -} - -if (isFG3()) { - // Include resources generated by data generators. - sourceSets.main.resources { srcDir 'src/generated/resources' } -} - -repositories { - mavenLocal() - mavenCentral() - maven { - name = 'jcenter' - url = 'https://jcenter.bintray.com/' - } - removeIf { it instanceof MavenArtifactRepository && it.url.host == 'files.minecraftforge.net' } - removeIf { it instanceof MavenArtifactRepository && it.url.host == 'maven.minecraftforge.net' } - maven { - name = 'forge' - url = 'https://maven.minecraftforge.net/' - } - maven { - name 'MMD' - url 'https://maven.mcmoddev.com/' + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + } } } dependencies { - implementation 'org.jetbrains:annotations:20.1.0' - if (isFG3()) { - minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" - } - - testImplementation('org.junit.platform:junit-platform-launcher:1.7.+') // testCompile - testImplementation('org.junit.jupiter:junit-jupiter-api:5.7.+') // testCompile - testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.+') // testRuntime - if (gradleNewerThan('2.13')) { - testCompileOnly 'junit:junit:4.13.2' - } - testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.7.+' //testRuntime - testImplementation('org.mockito:mockito-core:2.+') // testCompile - testImplementation('org.mockito:mockito-junit-jupiter:2.+') { // testCompile - exclude group: 'org.junit.jupiter', module: 'junit-jupiter-engine' - } - if (gradleNewerThan('4.6')) { - annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' - } - annotationProcessor 'org.spongepowered:mixin:0.8:processor' -} - -processResources { - if (!isFG3()) { - inputs.property 'version', project.short_version - inputs.property 'mcversion', project.minecraft.version - - from (sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version': project.short_version, 'mcversion': project.minecraft.version - } - - from (sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } - - rename '(.+_at.cfg)', 'META-INF/$1' - } -} - -jar { - //from sourceSets.api.output - //from sourceSets.main.output - - manifest { - mainAttributes( - 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}", - 'Timestamp': System.currentTimeMillis(), - 'Specification-Title': project.modid, - 'Specification-Vendor': project.vendor, - 'Specification-Version': '1', - 'Implementation-Title': project.archivesBaseName, - 'Implementation-Version': project.version, - 'Implementation-Vendor' : project.vendor, - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}", - 'MixinConfigs': "orespawn4.mixins.json" - ) - if (!isFG3()) { - if (project.hasProperty('core_plugin')) { - attributes 'FMLCorePluginContainsFMLMod': 'true' - attributes 'FMLCorePlugin': project.core_plugin - } - if (project.hasProperty('mod_at_file')) { - attributes 'FMLAT': project.mod_at_file - } - } - if (project.hasProperty('mod_contained_deps')) { - attributes 'ContainedDeps': project.mod_contained_deps - } - } -} - -if (!isFG3()) { -task apiJar(type: Jar) { - onlyIf { - getBooleanProperty('create_api_jar') - } - - description = 'Creates a JAR containing the API Classes.' - classifier = 'api' - from apiClasses - // from sourceSets.api.output - manifest { - mainAttributes( - 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:api", - 'Timestamp': System.currentTimeMillis(), - 'Specification-Title': project.modid, - 'Specification-Vendor': project.vendor, - 'Specification-Version': '1', - 'Implementation-Title': project.archivesBaseName + '-api', - 'Implementation-Version': project.version, - 'Implementation-Vendor' : project.vendor, - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}" - ) - } -} + implementation 'org.jetbrains:annotations:20.1.0' ; + minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" ; + annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' ; + annotationProcessor 'org.spongepowered:mixin:0.8:processor' ; } -task deobfJar(type: Jar) { - onlyIf { - getBooleanProperty('create_deobf_jar') - } - - description = 'Creates a JAR containing the non-obfuscated compiled code.' - classifier = 'deobf' - from sourceSets.main.output - manifest { - mainAttributes( - 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:deobf", - 'Timestamp': System.currentTimeMillis(), - 'Specification-Title': project.modid, - 'Specification-Vendor': project.vendor, - 'Specification-Version': '1', - 'Implementation-Title': project.archivesBaseName + '-deobf', - 'Implementation-Version': project.version, - 'Implementation-Vendor' : project.vendor, - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}" - ) - if (project.hasProperty('mod_at_file')) { - attributes 'FMLAT': project.mod_at_file - } - if (project.hasProperty('core_plugin')) { - attributes 'FMLCorePlugin': project.core_plugin - } - } -} - -if (!isFG2()) { -task sourceJar(type: Jar, dependsOn: classes) { - onlyIf { - getBooleanProperty('create_source_jar') - } - - description = 'Creates a JAR containing the source code.' - classifier = 'sources' - from sourceSets.main.allSource - manifest { - mainAttributes( - 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:sources", - 'Timestamp': System.currentTimeMillis(), - 'Specification-Title': 'examplemod', - 'Specification-Vendor': 'mcmoddev', - 'Specification-Version': '1', - 'Implementation-Title': project.name + '-sources', - 'Implementation-Version': project.version, - 'Implementation-Vendor' :'mcmoddev', - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}" - ) - } -} -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - onlyIf { - getBooleanProperty('create_javadoc_jar') - } - - description = 'Creates a JAR containing the JavaDocs.' - classifier = 'javadoc' - from javadoc.getDestinationDir() - manifest.mainAttributes( - 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}:javadoc", - 'Timestamp': System.currentTimeMillis(), - 'Specification-Title': project.modid, - 'Specification-Vendor': project.vendor, - 'Specification-Version': '1', - 'Implementation-Title': project.archivesBaseName + '-javadoc', - 'Implementation-Version': project.version, - 'Implementation-Vendor' : project.vendor, - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java': "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On': "${project.forge_mc_version}-${project.forge_version}" - ) -} - -def signType -if (isFG1() || isFG5()) { - signType = Jar -} else { - signType = SignJar - - task signJar(type: SignJar, dependsOn: jar) { - onlyIf { - getBooleanProperty('do_sign_jar') && - project.hasProperty('keyStore') - } - - description = 'Sign the Main JAR' - inputFile = jar.archivePath - outputFile = jar.archivePath - - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - } - - if (!isFG3()) { - task signApiJar(type: SignJar) { - onlyIf { - getBooleanProperty('create_api_jar') && - getBooleanProperty('do_sign_jar') && - project.hasProperty('keyStore') - } - - description = 'Sign the API JAR' - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - - inputFile = apiJar.archivePath - outputFile = apiJar.archivePath - } - } - - task signDeobfJar(type: SignJar) { - onlyIf { - getBooleanProperty('create_deobf_jar') && - getBooleanProperty('do_sign_jar') && - project.hasProperty('keyStore') - } - - description = 'Sign the deobf JAR' - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - - inputFile = deobfJar.archivePath - outputFile = deobfJar.archivePath - } - - task signSourceJar(type: SignJar) { - onlyIf { - getBooleanProperty('create_source_jar') && - getBooleanProperty('do_sign_jar') && - project.hasProperty('keyStore') - } - - description = 'Sign the Sources JAR' - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - -// if (!isFG3()) { - inputFile = sourceJar.archivePath - outputFile = sourceJar.archivePath -// } else { -// inputFile = sourcesJar.archivePath -// outputFile = sourcesJar.archivePath -// } - } - - task signJavadocJar(type: SignJar) { - onlyIf { - getBooleanProperty('create_javadoc_jar') && - getBooleanProperty('do_sign_jar') && - project.hasProperty('keyStore') - } - - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - - inputFile = javadocJar.archivePath - outputFile = javadocJar.archivePath - } -} - -if (getBooleanProperty('do_sign_jar') && (!isFG1())) { - build.dependsOn signJar - if (getBooleanProperty('create_api_jar') && !isFG3()) { - build.dependsOn signApiJar - } - if (getBooleanProperty('create_deobf_jar')) { - build.dependsOn signDeobfJar - } - if (getBooleanProperty('create_source_jar')) { -// if (!isFG3()) { - build.dependsOn signSourceJar -// } else { -// build.dependsOn signSourcesJar -// } - } - if (getBooleanProperty('create_javadoc_jar')) { - build.dependsOn signJavadocJar - } -} - -// TODO Test this -// Example configuration to allow publishing using the maven-publish task -// This is the preferred method to reobfuscate your jar file -jar.finalizedBy('reobfJar') -// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing -publish.dependsOn('reobfJar') -//tasks.publish.dependsOn build -tasks.curseforge.dependsOn publish - -artifacts { - if (getBooleanProperty('create_api_jar') && !isFG3()) { - archives apiJar - } - if (getBooleanProperty('create_deobf_jar')) { - archives deobfJar - } - if (getBooleanProperty('create_source_jar')) { -// if (!isFG3()) { - archives sourceJar -// } else { -// archives sourcesJar -// } - } - if (getBooleanProperty('create_javadoc_jar')) { - archives javadocJar - } -} - -//TODO: From Forge MDK -//def reobfFile = file("$buildDir/reobfJar/output.jar") -//def reobfArtifact = artifacts.add('default', reobfFile) { -// type 'jar' -// builtBy 'reobfJar' -//} - -publishing { - publications { - mavenJava(MavenPublication) { - //TODO: From Forge MDK - //artifact jar - //artifact reobfArtifact - //artifact reobfJar - groupId project.group - artifactId project.archivesBaseName - version project.version - from components.java - if (getBooleanProperty('create_source_jar')) { -// if (!isFG3()) { - artifact sourceJar { - classifier 'sources' - } -// } else { -// artifact sourcesJar { -// classifier 'sources' -// } -// } - } - if (getBooleanProperty('create_api_jar') && !isFG3()) { - artifact apiJar { - classifier 'api' - } - } - if (getBooleanProperty('create_deobf_jar')) { - artifact deobfJar { - classifier 'deobf' - } - } - if (getBooleanProperty('create_javadoc_jar')) { - artifact javadocJar { - classifier 'javadoc' - } - } - if (getBooleanProperty('pom_information') && gradleNewerThan('4.8')) { - pom { - name = project.name - description = project.description - url = project.pom_url - packaging = 'jar' - //inceptionYear = '' - if (getBooleanProperty('pom_license_information')) { - licenses { - license { - name = project.pom_license_name - url = project.pom_license_url - distribution = 'repo' - comments = project.pom_comments - } - } - } - if (getBooleanProperty('pom_scm_information')) { - scm { - connection = project.pom_scm_connection - developerConnection = project.pom_scm_developer_connection - // tag - url = project.pom_scm_url - } - } - if (getBooleanProperty('pom_issue_information')) { - issueManagement { - system = project.pom_issue_system - url = project.pom_issue_url - } - } - if (getBooleanProperty('pom_ci_information')) { - ciManagement { - system = project.pom_ci_system - url = project.pom_ci_url - } - } - if (getBooleanProperty('pom_organisation_information')) { - organization { - name = project.pom_organization_name - url = project.pom_organization_url - } - } - // contributors, developers, distributionManagement - } - } - } - } - repositories { - maven { - if ((secret.username != '') && (secret.password != '')) { - credentials { - username secret.username - password secret.password - } - } - if (secret.url != '') { - url secret.url - } else { - url "file:///${project.buildDir}/repo" - //TODO: From Forge MDK - //url "file:///${project.projectDir}/mcmodsrepo" - } - } - } -} - -curseforge { - if (project.hasProperty('curseforge_project_id') && getBooleanProperty('curseforge_do_upload')) { - apiKey = secret.curseforgeAPIKey - project { - id = project.curseforge_project_id - if (project.hasProperty('curseforge_changelog_filename')) { - changelog = file(project.curseforge_changelog_filename) - } else { - changelog = '' - } - if (project.hasProperty('curseforge_changelog_type')) { - changelogType = project.curseforge_changelog_type - } - if ((project.hasProperty('curseforge_release_type')) && ((project.curseforge_release_type == 'alpha') || (project.curseforge_release_type == 'beta') || (project.curseforge_release_type == 'release'))) { - releaseType = project.curseforge_release_type - } else { - releaseType = 'alpha' - } - if (project.hasProperty('curseforge_versions')) { - final def versions = "${project.curseforge_versions}".split(', ') - versions.each { - addGameVersion "${it}" - } - } - mainArtifact(jar) { - if (getBooleanProperty('curseforge_use_custom_display_name')) { - displayName = "${project.name} ${project.display_version}" - } - if (project.hasProperty('curseforge_requirements') || project.hasProperty('curseforge_optionals') || project.hasProperty('curseforge_embeddeds') || project.hasProperty('curseforge_tools') || project.hasProperty('curseforge_incompatibles') || project.hasProperty('curseforge_includes')) { - relations { - if (project.hasProperty('curseforge_requirements')) { - final def requirements = "${project.curseforge_requirements}".split(', ') - requirements.each { - requiredDependency "${it}" - } - } - if (project.hasProperty('curseforge_optionals')) { - final def optionals = "${project.curseforge_optionals}".split(', ') - optionals.each { - optionalDependency "${it}" - } - } - if (project.hasProperty('curseforge_embeddeds')) { - final def embeddeds = "${project.curseforge_embeddeds}".split(', ') - embeddeds.each { - embeddedLibrary "${it}" - } - } - if (project.hasProperty('curseforge_tools')) { - final def tools = "${project.curseforge_tools}".split(', ') - tools.each { - tool "${it}" - } - } - if (project.hasProperty('curseforge_incompatibles')) { - final def incompatibles = "${project.curseforge_incompatibles}".split(', ') - incompatibles.each { - incompatible "${it}" - } - } - } - } - } - if (getBooleanProperty('create_api_jar') && !isFG3()) { - addArtifact(apiJar) { - if (getBooleanProperty('curseforge_use_custom_display_name')) { - displayName = "${project.name} ${project.display_version} API" - } - } - } - if (getBooleanProperty('create_source_jar')) { -// if (!isFG3()) { - addArtifact(sourceJar) { - if (getBooleanProperty('curseforge_use_custom_display_name')) { - displayName = "${project.name} ${project.display_version} Sources" - } - } -// } else { -// addArtifact(sourcesJar) { -// if (getBooleanProperty('curseforge_use_custom_display_name')) { -// displayName = "${project.name} ${project.display_version} Sources" -// } -// } -// } - } - if (getBooleanProperty('create_deobf_jar')) { - addArtifact(deobfJar) { - if (getBooleanProperty('curseforge_use_custom_display_name')) { - displayName = "${project.name} ${project.display_version} Development" - } - } - } - if (getBooleanProperty('create_javadoc_jar')) { - addArtifact(javadocJar) { - if (getBooleanProperty('curseforge_use_custom_display_name')) { - displayName = "${project.name} ${project.display_version} Javadoc" - } - } - } - } - options { - debug = getBooleanProperty('curseforge_debug') - javaVersionAutoDetect = true - detectNewerJava = false - javaIntegration = true - forgeGradleIntegration = true - } - } -} - -sonarqube { - properties { - if (project.hasProperty('sonar_host_url')) { - property 'sonar.host.url', project.sonar_host_url - } else if (secret.sonarHost != '') { - property 'sonar.host.url', secret.sonarHost - } else { - property 'sonar.host.url', 'https://sonarcloud.io' - } - - if (secret.sonarToken != '') { - property 'sonar.login', secret.sonarToken - } - - if (project.hasProperty('sonar_organisation')) { - property 'sonar.organization', project.sonar_organisation - } else if (secret.sonarOrganisation != '') { - property 'sonar.organization', secret.sonarOrganization - } else { - property 'sonar.organization', 'unset' - } - - if (project.hasProperty('sonar_project_key')) { - property 'sonar.projectKey', project.sonar_project_key - } - - if (project.hasProperty('sonar_project_name')) { - property 'sonar.projectName', project.sonar_project_name - } - - if (project.hasProperty('sonar_project_description')) { - property 'sonar.projectDescription', project.sonar_project_description - } - - if (project.hasProperty('sonar_project_version')) { - property 'sonar.projectVersion', project.sonar_project_version - } - - if (project.hasProperty('sonar_branch_name')) { - property 'sonar.branch.name', project.sonar_branch_name - } - - if (project.hasProperty('sonar_branch_target')) { - property 'sonar.branch.target', project.sonar_branch_target - } - - property 'sonar.junit.reportPaths', "${project.buildDir}/test-results/junit-platform/*.xml" - property 'sonar.jacoco.reportPaths', "${project.buildDir}/jacoco/junitPlatformTest.exec" - //property 'sonar.groovy.jacoco.reportPath', "" - } -} - -String getModFile() { - String path = '' - final FileTree tree = fileTree(dir: 'src/main/java') - tree.include '**/*.java' - tree.visit { final element -> - if (element.file.isFile()) { - element.file.eachLine { final String s -> - final String sTrim = s.trim() - if (sTrim.startsWith('@Mod(')) { - path = "src/main/java/${element.relativePath}" - } - } - } - } - return path -} - -String getModVersion(final String type) { - return getModVersion(type, modFile) -} - -import java.util.regex.Matcher -String getModVersion(final String type, final String modFile) { - if ((modFile != null) && (modFile != '')) { - final File file = file(modFile) - final def prefix = ~/^(?:\s*)(?:(?public|protected|private) )?(?:(?static) )?(?:(?final) )?String $type = "(?:(?\d*)\.)?(?:(?\d*)\.)?(?:(?[^."]*)\.?)(?[^"]*)";$/ - for (final String s in file.readLines()) { - final Matcher matcher = (s.trim() =~ prefix) - if (matcher.matches()) { - return "${matcher.group('major') ?: '0'}.${matcher.group('minor') ?: '0'}.${matcher.group('patch')}" - } - } - } - return '0.0.0' -} - -boolean isFG1() { - return (project.fg_version == '1.0-SNAPSHOT') || (project.fg_version == '1.1-SNAPSHOT') || (project.fg_version == '1.2-SNAPSHOT') -} - -boolean isFG2() { - return (project.fg_version == '2.0-SNAPSHOT') || (project.fg_version == '2.1-SNAPSHOT') || (project.fg_version == '2.2-SNAPSHOT') || (project.fg_version == '2.3-SNAPSHOT') -} - -boolean isFG3() { - return (!isFG1() && !isFG2()) -} - -boolean isFG5() { - return true -// return (!isFG1() && !isFG2()) -} - -static boolean gradleOlderThan(final String version) { - return GradleVersion.current() < GradleVersion.version(version) -} - -static boolean gradleNewerThan(final String version) { - return GradleVersion.current() >= GradleVersion.version(version) -} - -static boolean isCi() { - return System.getenv('CI') -} - -boolean getBooleanProperty(final String property) { - if (project.hasProperty(property)) { - return ((String) project.findProperty(property)).toBoolean() - } else { - return false - } -} - -if (gradleNewerThan('2.1')) { - task gitChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) { - file = new File('CHANGELOG.md') - gitHubIssuePattern = "nonada123" - templateContent = file('changelog.mustache').getText('UTF-8') - } -} - -Object findProperty(final String propertyName) { - return hasProperty(propertyName) ? property(propertyName) : null -} - -/* -checkerFramework { - checkers = [ -// 'org.checkerframework.checker.units.UnitsChecker', - 'org.checkerframework.checker.nullness.NullnessChecker' - ] -} -*/ jar { manifest { attributes([ - // Existing properties here - "MixinConfigs": "orespawn4.mixins.json" + 'Maven-Artifact' : "${project.group}:${project.archivesBaseName}:${project.version}", + 'Timestamp' : System.currentTimeMillis(), + 'Specification-Title' : project.modid, + 'Specification-Vendor' : project.vendor, + 'Specification-Version' : '1', + 'Implementation-Title' : project.archivesBaseName, + 'Implementation-Version' : project.version, + 'Implementation-Vendor' : project.vendor, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Built-On-Java' : "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On' : "${project.forge_mc_version}-${project.forge_version}", + 'MixinConfigs' : "orespawn4.mixins.json" ]) } } diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java index f43324b..34f49db 100644 --- a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java +++ b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java @@ -13,5 +13,6 @@ public interface BiomeGenerationSettingsAccessor { @Accessor @Mutable + // List>>> void setFeatures(List>>> features); } From 314b7d9b5da52b1a4d4b37937a34375734cfcef7 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 19:55:18 -0500 Subject: [PATCH 32/37] need to specifically use version 0.8.3 or 0.8.4 of the mixin annotation processor with FG5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 367d21d..94267ac 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ dependencies { implementation 'org.jetbrains:annotations:20.1.0' ; minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" ; annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' ; - annotationProcessor 'org.spongepowered:mixin:0.8:processor' ; + annotationProcessor 'org.spongepowered:mixin:0.8.4:processor' ; } jar { From d4ed61d8c50e024b11ed3b79f55b6559db3052be Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Thu, 11 Nov 2021 20:34:42 -0500 Subject: [PATCH 33/37] Last commit for this line of progression - mixins and pure datapack stuff is broken completely in Forge, at this time... Going to revert back to the `load from custom JSON` setup and have everything done in `FMLCommonSetup` moving forward. It's just easier, overall --- build.gradle | 70 ++++++++++++------- .../java/com/mcmoddev/orespawn/OreSpawn.java | 2 + 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/build.gradle b/build.gradle index 94267ac..4a22ccd 100644 --- a/build.gradle +++ b/build.gradle @@ -45,32 +45,50 @@ minecraft { mappings channel: project.mcp_mappings_channel, version: project.mcp_mappings_version runs { - client { - workingDirectory project.file('run') - - taskName 'runClient' - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - - mods {} - } - - server { - workingDirectory project.file('run') - - taskName 'runServer' - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - - } - - data { - workingDirectory project.file('run') - - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') - } + client { + workingDirectory project.file('run') + arg '-mixin.config=orespawn4.mixins.json' + taskName 'runClient' + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + + mods { + orespawn4 { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + arg '-mixin.config=orespawn4.mixins.json' + + taskName 'runServer' + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + + mods { + orespawn4 { + source sourceSets.main + } + } + + } + + data { + workingDirectory project.file('run') + + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/'), '-mixin.config', 'orespawn4.mixins.json' + + mods { + orespawn4 { + source sourceSets.main + } + } + + } } } diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 73dfdae..d2df8d5 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -45,7 +45,9 @@ public void start(final FMLServerAboutToStartEvent event) { List>> cc = new LinkedList<>(); DefaultFeatureConfig.getMyFeatures().values().stream().forEach( cf -> cc.add(() -> cf)); data.add(cc); + LOGGER.fatal("Trying to reset biome features from %s to %s", settings.getFeatures(), data); ((BiomeGenerationSettingsAccessor) settings).setFeatures(data); + LOGGER.fatal("Do the two things match? %s", settings.getFeatures().containsAll(data) ); }); } From 1c67882815ed46b395c58e28aa5180063e2fc7d1 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Mon, 7 Feb 2022 07:26:10 -0500 Subject: [PATCH 34/37] more works, lots of stuff likely to move or disappear at a moments notice - nothing to see here, carry on\! --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 130 ++++++++++-------- .../com/mcmoddev/orespawn/data/Constants.java | 5 +- .../com/mcmoddev/orespawn/utils/Codecs.java | 39 ++++++ .../orespawn/utils/ResourceLoader.java | 65 +++++++++ 4 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Codecs.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index d2df8d5..43f5600 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,65 +1,87 @@ -package com.mcmoddev.orespawn; + package com.mcmoddev.orespawn; -import com.mcmoddev.orespawn.utils.mixins.BiomeGenerationSettingsAccessor; -import com.mcmoddev.orespawn.utils.mixins.ServerAccessor; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.biome.BiomeGenerationSettings; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.Feature; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + import com.google.common.base.Joiner; + import com.mcmoddev.orespawn.data.Constants; + import net.minecraft.util.ResourceLocation; + import net.minecraft.world.gen.feature.Feature; + import net.minecraftforge.event.RegistryEvent; + import net.minecraftforge.eventbus.api.SubscribeEvent; + import net.minecraftforge.fml.ModList; + import net.minecraftforge.fml.common.Mod; + import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; + import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; + import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Supplier; + import java.io.IOException; + import java.nio.file.Files; + import java.nio.file.Path; + import java.util.ArrayList; + import java.util.List; + import java.util.Locale; + import java.util.stream.Collectors; -import com.mcmoddev.orespawn.world.features.Features; -import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; + @Mod("orespawn4") + public class OreSpawn { + // Directly reference a log4j logger. + public static final Logger LOGGER = LogManager.getFormatterLogger(); -@Mod("orespawn4") -public class OreSpawn { - // Directly reference a log4j logger. - public static final Logger LOGGER = LogManager.getFormatterLogger(); + public OreSpawn() { + // Register the setup method for modloading + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); - public OreSpawn() { - // Register the setup method for modloading - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); - MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::start); -// MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::finalizeFeatureRegistration); - } + } - public void start(final FMLServerAboutToStartEvent event) { - ((ServerAccessor) event.getServer()).getDynamicRegistries().getRegistry(Registry.BIOME_KEY).stream().forEach(b -> { - BiomeGenerationSettings settings = b.getGenerationSettings(); - List>>> data = new LinkedList<>(); - data.addAll(settings.getFeatures()); - List>> cc = new LinkedList<>(); - DefaultFeatureConfig.getMyFeatures().values().stream().forEach( cf -> cc.add(() -> cf)); - data.add(cc); - LOGGER.fatal("Trying to reset biome features from %s to %s", settings.getFeatures(), data); - ((BiomeGenerationSettingsAccessor) settings).setFeatures(data); - LOGGER.fatal("Do the two things match? %s", settings.getFeatures().containsAll(data) ); - }); - } + private void setup(final FMLCommonSetupEvent event) { + List foundFiles = new ArrayList<>(); + ModList.get().getModFiles().stream() + .map(ModFileInfo::getFile) + .map(modFile -> { + Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); - private void setup(final FMLCommonSetupEvent event) { - } + try { + return Files.walk(root). + map(path -> root.relativize(path.toAbsolutePath())). + filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds + filter(path -> path.toString().endsWith(".json")). + map(path -> Joiner.on('/').join(path)). + //map(path -> path.toString()). + map(path -> path.substring(0, path.length() - 5)). + map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)). + collect(Collectors.toList()); + } catch (IOException e) { + LOGGER.error("Exception trying to get possible data from mod-resources: {}", e.getMessage()); + return new ArrayList(); + } + }) + .filter(lrl -> !lrl.isEmpty()) + .forEach(foundFiles::addAll); + + try { + Path diskPath = Constants.JSONPATH.toAbsolutePath(); + List temp = Files.walk(diskPath) + .map(path -> diskPath.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64) + .filter(path -> path.toString().endsWith(".json")) + .map(path -> Joiner.on('/').join(path)) + //.map(path -> path.toString()) + .map(path -> path.toLowerCase(Locale.US)) + .map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation("orespawn-disk", path)) + .collect(Collectors.toList()); + foundFiles.addAll(temp); + } catch (IOException e) { + LOGGER.error("Exception trying to get possible data from config directory resources: {}", e.getMessage()); + } + foundFiles.forEach(rl -> LOGGER.debug("Found possible data with handle {}", rl.toString())); + } - @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) - public static class RegistryEvents { - @SubscribeEvent - public static void featureRegistryEvent(final RegistryEvent.Register> event) { - event.getRegistry().register(Features.DEFAULT.setRegistryName("default")); - LOGGER.info("Registered %s", Features.DEFAULT.getRegistryName()); + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class RegistryEvents { + @SubscribeEvent + public static void featureRegistryEvent(final RegistryEvent.Register> event) { + } } } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index c413fc8..d5bad7b 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -31,7 +31,10 @@ public static class FileBits { public static final String ALLOWED_MODS = "active_mods.json"; public static final String DISK = "__DISK__"; public static final String RESOURCE = "__RESOURCE__"; - public static final String RESOURCE_PATH = "/assets/orespawn-data"; + public static final String ASSETS_DIR = "assets"; + public static final String ORESPAWN_DIR = "orespawn-data"; + public static final Path ORESPAWN_RESOURCES = Paths.get(ASSETS_DIR, ORESPAWN_DIR); + public static final String RESOURCE_PATH = ORESPAWN_RESOURCES.toString(); } public enum FileTypes { FEATURES, SPAWN, PRESETS, REPLACEMENTS diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java b/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java new file mode 100644 index 0000000..2ea99ca --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java @@ -0,0 +1,39 @@ +package com.mcmoddev.orespawn.utils; + +import com.mojang.datafixers.util.*; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.function.Function; + +public class Codecs { + public final Codec EASY_STATE = Codec.either(BlockState.CODEC, Registry.BLOCK).xmap( + either -> either.map(Function.identity(), Block::getDefaultState), + state -> state.equals(state.getBlock().getDefaultState()) ? Either.right(state.getBlock()) : Either.left(state) + ); + + public final Codec> BLOCKSTATE_LIST = Codec.list(EASY_STATE); + public final Codec> RESOURCELOCATION_LIST = Codec.list(ResourceLocation.CODEC); + + public final class PredicateList { + public final Codec CODEC = RecordCodecBuilder.create( (base) -> { + return base.group( + ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> config.type), + RESOURCELOCATION_LIST.fieldOf("data").forGetter((config) -> config.data)) + .apply(base, PredicateList::new); + }); + + public final ResourceLocation type; + public final List data; + + public PredicateList(ResourceLocation type, List dataIn) { + this.type = type; + this.data = dataIn; + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java new file mode 100644 index 0000000..2ce6b4b --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java @@ -0,0 +1,65 @@ +package com.mcmoddev.orespawn.utils; + +import com.google.common.base.Joiner; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import com.mcmoddev.orespawn.data.Constants; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class ResourceLoader { + public ResourceLoader() { + List foundFiles = new LinkedList<>(); + + // get the files we're interested in + ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) + .forEach(foundFiles::addAll); + + // iterate and load! + foundFiles.parallelStream().forEach( (resLoc) -> { + Path filePath = resourceLocationToPath(resLoc); + try (InputStream dataStream = Files.newInputStream(filePath); + BufferedInputStream bis = new BufferedInputStream(dataStream); + InputStreamReader baseReader = new InputStreamReader(bis); + JsonReader dataReader = new JsonReader(baseReader)) { + loadFeature((new JsonParser()).parse(dataReader)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private void loadFeature(JsonElement parse) { + } + + private Path resourceLocationToPath(ResourceLocation location) { + ModFile modFile = ModList.get().getModFileById(location.getNamespace()).getFile(); + return modFile.getLocator().findPath(modFile, "assets", "orespawn4-data", String.format("{rl.getPath()}.json")); + } + private List iterateFiles(ModFile modFile) { + try { + Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); + + return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds + filter(path -> path.toString().endsWith(".json")).map(path -> Joiner.on('/').join(path)) + .map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) + .collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } +} From fc1bdc7ae0fa6c3b8e7e4ece7829efed36096228 Mon Sep 17 00:00:00 2001 From: "D. Hazelton" Date: Thu, 15 Sep 2022 16:11:56 -0400 Subject: [PATCH 35/37] Lots of churn to get really rolling on making this work properly. Lots still to do, but at least we have a proper start now. --- .../java/com/mcmoddev/orespawn/OreSpawn.java | 77 +++-------- .../com/mcmoddev/orespawn/api/OS4API.java | 7 - .../mcmoddev/orespawn/data/BiomeMatcher.java | 20 --- .../com/mcmoddev/orespawn/data/BlockType.java | 71 ---------- .../com/mcmoddev/orespawn/data/Config.java | 50 ++++++- .../orespawn/data/ConfigBlacklist.java | 31 ----- .../com/mcmoddev/orespawn/data/Constants.java | 7 +- .../data/DefaultFeatureParameters.java | 4 - .../orespawn/data/DimensionMatcher.java | 28 ---- .../com/mcmoddev/orespawn/utils/Codecs.java | 39 ------ .../com/mcmoddev/orespawn/utils/Helpers.java | 59 --------- .../com/mcmoddev/orespawn/utils/Loaders.java | 124 ++++++++++++++++++ .../orespawn/utils/ReflectionHelper.java | 61 --------- .../orespawn/utils/ResourceLoader.java | 65 --------- .../mcmoddev/orespawn/utils/TypeUtils.java | 16 --- .../utils/codecs/AllowDenyListBase.java | 53 -------- .../utils/codecs/BiomeMatcherConfig.java | 38 ------ .../utils/codecs/BlockTypeConfig.java | 21 --- .../DefaultFeatureParametersConfig.java | 28 ---- .../utils/codecs/DimensionMatcherConfig.java | 33 ----- .../BiomeGenerationSettingsAccessor.java | 18 --- .../orespawn/utils/mixins/ServerAccessor.java | 12 -- .../orespawn/world/WorldGenHandler.java | 29 ---- .../world/features/DefaultFeature.java | 56 -------- .../orespawn/world/features/Features.java | 11 -- .../gen/configs/DefaultFeatureConfig.java | 77 ----------- .../world/gen/configs/OreSpawnFeature.java | 24 ---- 27 files changed, 185 insertions(+), 874 deletions(-) delete mode 100644 src/main/java/com/mcmoddev/orespawn/api/OS4API.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/BlockType.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Codecs.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Helpers.java create mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Loaders.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/features/Features.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 43f5600..46e657d 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,87 +1,42 @@ package com.mcmoddev.orespawn; - import com.google.common.base.Joiner; - import com.mcmoddev.orespawn.data.Constants; - import net.minecraft.util.ResourceLocation; - import net.minecraft.world.gen.feature.Feature; - import net.minecraftforge.event.RegistryEvent; - import net.minecraftforge.eventbus.api.SubscribeEvent; - import net.minecraftforge.fml.ModList; + import com.mcmoddev.orespawn.data.Config; + import com.mcmoddev.orespawn.utils.Loaders; import net.minecraftforge.fml.common.Mod; + import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; - import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import java.io.IOException; - import java.nio.file.Files; - import java.nio.file.Path; - import java.util.ArrayList; - import java.util.List; - import java.util.Locale; - import java.util.stream.Collectors; - - @Mod("orespawn4") + @Mod("orespawn") public class OreSpawn { // Directly reference a log4j logger. public static final Logger LOGGER = LogManager.getFormatterLogger(); public OreSpawn() { // Register the setup method for modloading + // find and load the configs - for this we just use a custom class... +// Loaders.loadConfigs(); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); +// FMLJavaModLoadingContext.get().getModEventBus().addListener(this::modConfig); + // register features so they can be used elsewhere + Features.loadAndRegister(); + } +/* + public void modConfig(ModConfig.ModConfigEvent event) + { + ModConfig config = event.getConfig(); + Config.refresh(); } +*/ private void setup(final FMLCommonSetupEvent event) { - List foundFiles = new ArrayList<>(); - ModList.get().getModFiles().stream() - .map(ModFileInfo::getFile) - .map(modFile -> { - Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); - - try { - return Files.walk(root). - map(path -> root.relativize(path.toAbsolutePath())). - filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds - filter(path -> path.toString().endsWith(".json")). - map(path -> Joiner.on('/').join(path)). - //map(path -> path.toString()). - map(path -> path.substring(0, path.length() - 5)). - map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)). - collect(Collectors.toList()); - } catch (IOException e) { - LOGGER.error("Exception trying to get possible data from mod-resources: {}", e.getMessage()); - return new ArrayList(); - } - }) - .filter(lrl -> !lrl.isEmpty()) - .forEach(foundFiles::addAll); - - try { - Path diskPath = Constants.JSONPATH.toAbsolutePath(); - List temp = Files.walk(diskPath) - .map(path -> diskPath.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64) - .filter(path -> path.toString().endsWith(".json")) - .map(path -> Joiner.on('/').join(path)) - //.map(path -> path.toString()) - .map(path -> path.toLowerCase(Locale.US)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation("orespawn-disk", path)) - .collect(Collectors.toList()); - foundFiles.addAll(temp); - } catch (IOException e) { - LOGGER.error("Exception trying to get possible data from config directory resources: {}", e.getMessage()); - } - foundFiles.forEach(rl -> LOGGER.debug("Found possible data with handle {}", rl.toString())); } @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { - @SubscribeEvent - public static void featureRegistryEvent(final RegistryEvent.Register> event) { - } } } diff --git a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java b/src/main/java/com/mcmoddev/orespawn/api/OS4API.java deleted file mode 100644 index 0026ca8..0000000 --- a/src/main/java/com/mcmoddev/orespawn/api/OS4API.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mcmoddev.orespawn.api; - -public class OS4API { - private OS4API() { - // purposefully blank and private - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java deleted file mode 100644 index 5bda486..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/BiomeMatcher.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.utils.codecs.AllowDenyListBase; -import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; -import net.minecraft.util.RegistryKey; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.biome.Biome; - -public class BiomeMatcher extends AllowDenyListBase>> { - private final BiomeMatcherConfig myConfig; - - public BiomeMatcher(final BiomeMatcherConfig config) { - super( config.type, Registry.BIOME_KEY, config.data ); - myConfig = config; - } - - public BiomeMatcherConfig getConfig() { - return myConfig; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/BlockType.java b/src/main/java/com/mcmoddev/orespawn/data/BlockType.java deleted file mode 100644 index 456f86a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/BlockType.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.utils.codecs.BlockTypeConfig; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import net.minecraft.util.ResourceLocation; - -import java.util.Locale; - -public class BlockType { - private final BlockTypeType type; - private final ResourceLocation blockLoc; - - public BlockType(final BlockTypeConfig conf) { - this.type = conf.type; - this.blockLoc = conf.name; - } - - public BlockTypeType getType() { - return this.type; - } - - public ResourceLocation getName() { - return this.blockLoc; - } - - public enum BlockTypeType { - TAG("TAG"), BLOCK("BLOCK"); - - - public static final Codec CODEC = Codec.STRING.comapFlatMap(BlockTypeType::myValueOf, BlockTypeType::asString).stable(); - - private final String text; - - /** - * @param text - */ - BlockTypeType(final String text) { - this.text = text; - } - - public static DataResult myValueOf(final String value) { - final String lookup = value.toUpperCase(Locale.US); - DataResult res; - - switch(lookup) { - case "TAG": - res = DataResult.success(TAG); - break; - case "BLOCK": - res = DataResult.success(BLOCK); - break; - default: - res = DataResult.error(String.format("Value %s is not valid for a type flag here", lookup)); - } - return res; - } - - /* (non-Javadoc) - * @see java.lang.Enum#toString() - */ - @Override - public String toString() { - return text; - } - - public static String asString( BlockTypeType val ) { - return val.toString(); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java index 42f9f4f..2376f44 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Config.java @@ -4,23 +4,49 @@ import org.apache.commons.lang3.tuple.Pair; public class Config { - static - { - final Pair specPair = new ForgeConfigSpec.Builder().configure(CommonConfig::new); - COMMON_SPEC = specPair.getRight(); - COMMON = specPair.getLeft(); - } + static + { + final Pair specPair = new ForgeConfigSpec.Builder().configure(CommonConfig::new); + COMMON_SPEC = specPair.getRight(); + COMMON = specPair.getLeft(); + } public static final ForgeConfigSpec COMMON_SPEC; public static final CommonConfig COMMON; + public static void refresh() { + } + public static class CommonConfig { + public final ForgeConfigSpec.BooleanValue replaceVanillaOreGeneration; + public final ForgeConfigSpec.BooleanValue replaceAllGeneration; + public final ForgeConfigSpec.BooleanValue enableRetrogeneration; + public final ForgeConfigSpec.BooleanValue forceEnableRetrogeneration; public final ForgeConfigSpec.BooleanValue flattenBedrock; public final ForgeConfigSpec.BooleanValue retroactivelyFlattenBedrock; public final ForgeConfigSpec.IntValue layersOfBedrock; + public final ForgeConfigSpec.BooleanValue extractToDisk; + public final ForgeConfigSpec.BooleanValue ignoreResources; + public final ForgeConfigSpec.BooleanValue ignoreDisk; CommonConfig(ForgeConfigSpec.Builder builder) { builder.push("general"); + replaceVanillaOreGeneration = builder + .comment("Attempt to override vanilla Minecraft ore generation when TRUE") + .translation("text.mmd_orespawn.config.replace_vanilla") + .define("Replace Vanilla Oregen", false); + replaceAllGeneration = builder + .comment("Attempt to replace all ore generation, even from other mods, when TRUE") + .translation("text.mmd_orespawn.config.replace_all") + .define("Replace All Generation", false); + enableRetrogeneration = builder + .comment("Attempt to generate new spawns in chunks that were previously generated or were generated with different configuration options when TRUE") + .translation("text.mmd_orespawn.config.retrogen") + .define("Retrogen", false); + forceEnableRetrogeneration = builder + .comment("Force retroactive generation of new spawns, even if the feature is configured to not perform it when TRUE") + .translation("text.mmd_orespawn.config.force_retrogen") + .define("Force Retrogen", false); flattenBedrock = builder .comment("Make the bedrock flat in chunks generated when this option is TRUE") .translation("text.mmd_orespawn.config.flatten_bedrock") @@ -33,6 +59,18 @@ public static class CommonConfig { .comment("Attempt flatten the bedrock in chunks generated before this option and the \"Flatten Bedrock\" option were set to TRUE") .translation("text.mmd_orespawn.config.retro_bedrock") .define("Retrogen Flat Bedrock", false); + extractToDisk = builder + .comment("Extract all integration configurations found to \"config/mmd-orespawn-4\" when TRUE") + .translation("text.mmd_orespawn.config.extract_integration") + .define("Extract Files", false); + ignoreResources = builder + .comment("Do not attempt to locate or use any integration configurations when TRUE") + .translation("text.mmd_orespawn.config.ignore_integration") + .define("Ignore Integration", false); + ignoreDisk = builder + .comment("Do not attempt to load any config files that are on disk when TRUE") + .translation("text.mmd_orespawn.config.ignore_disk") + .define("Ignore Config Files On Disk", false); } } } diff --git a/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java b/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java deleted file mode 100644 index 0676dc8..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/ConfigBlacklist.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.google.gson.JsonElement; - -import java.util.List; -import java.util.LinkedList; - -public class ConfigBlacklist { - private static final List blockedMods = new LinkedList<>(); - private static final List blockedConfigs = new LinkedList<>(); - - public ConfigBlacklist() { - - } - - public void addMod(final String modid) { - blockedMods.add(modid); - } - - public void addConfig(final String configName) { - blockedConfigs.add(configName); - } - - public boolean isAllowed(final String checkName) { - return !(blockedMods.contains(checkName) || blockedConfigs.contains(checkName)); - } - - public boolean isBlocked(final String checkName) { - return !isAllowed(checkName); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java index d5bad7b..cb24c49 100644 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ b/src/main/java/com/mcmoddev/orespawn/data/Constants.java @@ -8,8 +8,6 @@ public class Constants { public static final String CRASH_SECTION = "OreSpawn Version"; public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4, FileBits.SYSCONF); public static final Path JSONPATH = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4); - public static final String MODID = "mmd_orespawn"; - public static class ConfigNames { public static final String REPLACEMENT = "replaces"; @@ -31,10 +29,7 @@ public static class FileBits { public static final String ALLOWED_MODS = "active_mods.json"; public static final String DISK = "__DISK__"; public static final String RESOURCE = "__RESOURCE__"; - public static final String ASSETS_DIR = "assets"; - public static final String ORESPAWN_DIR = "orespawn-data"; - public static final Path ORESPAWN_RESOURCES = Paths.get(ASSETS_DIR, ORESPAWN_DIR); - public static final String RESOURCE_PATH = ORESPAWN_RESOURCES.toString(); + public static final String RESOURCE_PATH = "/assets/orespawn-data"; } public enum FileTypes { FEATURES, SPAWN, PRESETS, REPLACEMENTS diff --git a/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java b/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java deleted file mode 100644 index 0f413ae..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/DefaultFeatureParameters.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.mcmoddev.orespawn.data; - -public class DefaultFeatureParameters { -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java b/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java deleted file mode 100644 index 967e99c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/DimensionMatcher.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import com.mcmoddev.orespawn.utils.codecs.AllowDenyListBase; -import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.Dimension; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import static com.mcmoddev.orespawn.utils.Helpers.makeBlockResourceLocation; - -public class DimensionMatcher extends AllowDenyListBase>> { - private final DimensionMatcherConfig myConfig; - - public DimensionMatcher(final DimensionMatcherConfig config) { - super(config.type, Registry.DIMENSION_KEY, config.data); - myConfig = config; - } - - public DimensionMatcherConfig getConfig() { - return myConfig; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java b/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java deleted file mode 100644 index 2ea99ca..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/Codecs.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import com.mojang.datafixers.util.*; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; - -import java.util.List; -import java.util.function.Function; - -public class Codecs { - public final Codec EASY_STATE = Codec.either(BlockState.CODEC, Registry.BLOCK).xmap( - either -> either.map(Function.identity(), Block::getDefaultState), - state -> state.equals(state.getBlock().getDefaultState()) ? Either.right(state.getBlock()) : Either.left(state) - ); - - public final Codec> BLOCKSTATE_LIST = Codec.list(EASY_STATE); - public final Codec> RESOURCELOCATION_LIST = Codec.list(ResourceLocation.CODEC); - - public final class PredicateList { - public final Codec CODEC = RecordCodecBuilder.create( (base) -> { - return base.group( - ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> config.type), - RESOURCELOCATION_LIST.fieldOf("data").forGetter((config) -> config.data)) - .apply(base, PredicateList::new); - }); - - public final ResourceLocation type; - public final List data; - - public PredicateList(ResourceLocation type, List dataIn) { - this.type = type; - this.data = dataIn; - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java b/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java deleted file mode 100644 index ad6d2a3..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/Helpers.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.Constants; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; - -import net.minecraft.block.BlockState; -import net.minecraft.command.arguments.BlockStateParser; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.Nullable; - -import java.nio.file.Path; -import java.nio.file.Paths; - -public class Helpers { - public static ResourceLocation makeResourceLocationFlat(final String domain, final String path) { - return new ResourceLocation(domain, path); - } - - private static Pair getNameBits(final String defaultDomain, final String toSplit) { - String domain; - String path; - if (toSplit.contains(":")) { - String[] bits = toSplit.split(":"); - domain = bits[0]; - path = bits[1]; - } else { - domain = defaultDomain; - path = toSplit; - } - - return Pair.of(domain, path); - } - public static ResourceLocation makeInternalResourceLocation(final String locIn) { - Pair bits = getNameBits("orespawn4", locIn); - return makeResourceLocationFlat(bits.getLeft(), bits.getRight()); - } - - public static ResourceLocation makeBlockResourceLocation(final String blockName) { - Pair bits = getNameBits("minecraft", blockName); - return makeResourceLocationFlat(bits.getLeft(), bits.getRight()); - } - - public static @Nullable BlockState deserializeState(final String fullState) { - @Nullable BlockState result; - try { - result = new BlockStateParser(new StringReader(fullState), false).parse(false).getState(); - } catch (CommandSyntaxException e) { - OreSpawn.LOGGER.error("Error parsing serialized BlockState {} - {}", fullState, e.getMessage()); - e.printStackTrace(); - result = null; - } - return result; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java b/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java new file mode 100644 index 0000000..d129df4 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java @@ -0,0 +1,124 @@ +package com.mcmoddev.orespawn.utils; + +import com.google.common.base.Joiner; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.data.Constants; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; + +import org.apache.commons.io.FileUtils; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static com.mcmoddev.orespawn.data.Config.COMMON; + +public class Loaders { + protected static List foundFiles = new LinkedList<>(); + private static Map loadedConfigs = new ConcurrentHashMap<>(); + + public static void loadConfigs() { + ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) + .forEach(foundFiles::addAll); + iterDisk().stream().forEach(foundFiles::add); + // okay, at this point we have things found, but not loaded - first we need a filter of the thing + // based on existing configs + // TODO: Custom config system needed here - this is before the Forge config stuff is ready + // TODO: Custom Config should be an extended JSON - see about doing it like some older stuff written in NodeJS + // and possibly some custom filters that can take basic expressions + if (COMMON.ignoreResources.get()) + foundFiles = foundFiles.stream().filter( rl -> rl.getNamespace() == "orespawn").collect(Collectors.toList()); + if (COMMON.ignoreDisk.get()) + foundFiles = foundFiles.stream().filter( rl -> rl.getNamespace() != "orespawn").collect(Collectors.toList()); + if (!foundFiles.isEmpty()) { + foundFiles = foundFiles.stream().filter( Loaders::runConfiguredFilters ).collect(Collectors.toList()); + } + // now that filtering is done, we can actually loop and load as needed + doActualLoad(); + } + + private static void doActualLoad() { + // doing this as a parallel run and using the 'ifAbsent' form to try and add some speed to the process + foundFiles.parallelStream().forEach( rl -> { + if (rl.getNamespace() == "orespawn") // on disk file + loadedConfigs.putIfAbsent(rl, loadFromDisk(rl)); + else // assume a resource from a mod + loadedConfigs.putIfAbsent(rl, loadAsResource(rl)); + }); + } + + private static JsonObject loadFromDisk(final ResourceLocation loc) { + Path p = Constants.JSONPATH.resolve(String.format("{}.json", loc.getPath())); + JsonParser parser = new JsonParser(); + try (FileInputStream baseInput = new FileInputStream(p.toFile()); + BufferedInputStream dataInput = new BufferedInputStream(baseInput); + InputStreamReader theReader = new InputStreamReader(dataInput)) { + return parser.parse(new JsonReader(theReader)).getAsJsonObject(); + } catch(IOException ex) { + OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); + ex.printStackTrace(); + return new JsonObject(); + } + } + + private static JsonObject loadAsResource(final ResourceLocation loc) { + ModFile mf = ModList.get().getModFileById(loc.getNamespace()).getFile(); + Path p = mf.getLocator().findPath(mf, "assets", "orespawn4-data", loc.getPath()+".json"); + try { + String fileData = FileUtils.readFileToString(p.toFile(), "UTF-8"); + JsonParser parser = new JsonParser(); + return parser.parse(fileData).getAsJsonObject(); + } catch(IOException ex) { + OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); + ex.printStackTrace(); + return new JsonObject(); + } + } + + private static boolean runConfiguredFilters(ResourceLocation resourceLocation) { + // TODO: Custom Config filters here + // TODO: Basic Whitelist/Blacklist here + return true; // placeholder/stub return + } + + private static List iterDisk() { + try { + return Files.walk(Constants.JSONPATH) + .map( path -> Constants.JSONPATH.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64) // Make sure the depth is within bounds + .filter( path -> path.toString().endsWith(".json")) + .filter( path -> !path.toString().contains(Constants.SYSCONF.toString())) + .map( path -> Joiner.on('/').join(path)) + .map( path -> new ResourceLocation("orespawn", path)) + .collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } + + private static List iterateFiles(ModFile modFile) { + try { + Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); + + return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) + .filter(path -> path.getNameCount() <= 64) // Make sure the depth is within bounds + .filter(path -> path.toString().endsWith(".json")) // check extension + .map(path -> Joiner.on('/').join(path)) + .map(path -> path.substring(0, path.length() - 5)) + .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) + .collect(Collectors.toList()); + } catch (IOException e) { + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java b/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java deleted file mode 100644 index 485d0f6..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/ReflectionHelper.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import com.mcmoddev.orespawn.OreSpawn; -//import com.mcmoddev.orespawn.api.OS4Feature; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - -public class ReflectionHelper { - public class OS4Feature {}; - public static Class getFeatureNamed(final String name, final String fqcn) { - Class resultBase; - Class result = null; - - try { - resultBase = Class.forName(fqcn); - if (resultBase.isAssignableFrom(OS4Feature.class)) result = (Class) resultBase; - } catch (ClassNotFoundException e) { - OreSpawn.LOGGER.error("Error in getting reference to class {} for feature {} -- {}", fqcn, name, e.getMessage()); - e.printStackTrace(); - return null; - } - - return result; - } - - public static Constructor getConstructorForFeature(Class clazz) { - Constructor constructor = null; - try { - constructor = clazz.getConstructor(); - } catch (NoSuchMethodException e) { - OreSpawn.LOGGER.error("Unable to get constructor for {} - {}", clazz.getCanonicalName(), e.getMessage()); - e.printStackTrace(); - return null; - } - - return constructor; - } - - public static OS4Feature getFeatureClassInstance(Constructor featureClassConstructor) { - try { - return featureClassConstructor.newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - OreSpawn.LOGGER.error("Could not instantiate an instance of {} -- {}", featureClassConstructor.getClass().getCanonicalName(), e.getMessage()); - e.printStackTrace(); - return null; - } - } - - public static OS4Feature getFeatureFromName(final String featureClassName, final String featureName) { - Class baseClass = getFeatureNamed(featureName, featureClassName); - Constructor classConstructor; - OS4Feature instance; - if (baseClass != null) classConstructor = getConstructorForFeature(baseClass); - else return null; - if (classConstructor != null) instance = getFeatureClassInstance(classConstructor); - else return null; - - return instance; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java b/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java deleted file mode 100644 index 2ce6b4b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/ResourceLoader.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import com.google.common.base.Joiner; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.stream.JsonReader; -import com.mcmoddev.orespawn.data.Constants; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -public class ResourceLoader { - public ResourceLoader() { - List foundFiles = new LinkedList<>(); - - // get the files we're interested in - ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) - .forEach(foundFiles::addAll); - - // iterate and load! - foundFiles.parallelStream().forEach( (resLoc) -> { - Path filePath = resourceLocationToPath(resLoc); - try (InputStream dataStream = Files.newInputStream(filePath); - BufferedInputStream bis = new BufferedInputStream(dataStream); - InputStreamReader baseReader = new InputStreamReader(bis); - JsonReader dataReader = new JsonReader(baseReader)) { - loadFeature((new JsonParser()).parse(dataReader)); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } - - private void loadFeature(JsonElement parse) { - } - - private Path resourceLocationToPath(ResourceLocation location) { - ModFile modFile = ModList.get().getModFileById(location.getNamespace()).getFile(); - return modFile.getLocator().findPath(modFile, "assets", "orespawn4-data", String.format("{rl.getPath()}.json")); - } - private List iterateFiles(ModFile modFile) { - try { - Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); - - return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64). // Make sure the depth is within bounds - filter(path -> path.toString().endsWith(".json")).map(path -> Joiner.on('/').join(path)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) - .collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java b/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java deleted file mode 100644 index 2488b6b..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/TypeUtils.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import org.objectweb.asm.Type; - -import com.mcmoddev.orespawn.OreSpawn; - -public class TypeUtils { - public static Class getClassFromType(Type type) { - try { - return Class.forName(type.getClassName(), false, OreSpawn.class.getClassLoader()); - } catch(ClassNotFoundException e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java deleted file mode 100644 index 27d450a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/AllowDenyListBase.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -public class AllowDenyListBase { - private final List listed = new LinkedList<>(); - private final ResourceLocation type; - private final T baseKey; - - public static final ResourceLocation whitelist = new ResourceLocation("orespawn4", "allowlist"); - public static final ResourceLocation blacklist = new ResourceLocation("orespawn4", "denylist"); - public static final ResourceLocation denyall = new ResourceLocation("orespawn4", "denyall"); - public static final ResourceLocation allowall = new ResourceLocation("orespawn4", "allowall"); - - protected AllowDenyListBase(final ResourceLocation matchType, final T key, List baseList) { - this.type = matchType; - this.baseKey = key; - listed.addAll((List) baseList.stream().map( rl -> (T) RegistryKey.getOrCreateKey(this.baseKey, rl)).collect(Collectors.toList())); - } - - public boolean matches(final String biomeName) { - if (type.equals(denyall) || (listed.isEmpty() && type.equals(whitelist))) return false; - else if (type.equals(allowall) || (listed.isEmpty() && type.equals(blacklist))) return true; - else return matches(makeResourceLocation(biomeName)); - } - - private ResourceLocation makeResourceLocation(final String name) { - String namespace = "minecraft"; - String biomeId = name; - - if (name.indexOf(':') > -1) { - String[] bits = name.split(":"); - namespace = bits[0]; - biomeId = bits[1]; - } - - return new ResourceLocation(namespace, biomeId); - } - - public boolean matches(final ResourceLocation biomeName) { - return matches((T)RegistryKey.getOrCreateKey(this.baseKey, biomeName)); - } - - private boolean matches(final T item) { - if (type == blacklist && listed.contains(item)) return false; - return listed.contains(item); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java deleted file mode 100644 index 2d71b1d..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BiomeMatcherConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import com.google.common.collect.ImmutableList; - -import com.mcmoddev.orespawn.data.BiomeMatcher; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; - -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; - -import java.util.Collections; -import java.util.List; - -public class BiomeMatcherConfig { - public final List data; - public final ResourceLocation type; - - public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(ResourceLocation.CODEC.listOf().fieldOf("data").forGetter((config) -> { - return config.data; - }), - ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { - return config.type; - })).apply(base, BiomeMatcherConfig::new); - }); - - public BiomeMatcherConfig(final List entryList, final ResourceLocation type) { - this.data = ImmutableList.copyOf(entryList); - this.type = type; - } - - public BiomeMatcherConfig() { - type = AllowDenyListBase.denyall; - data = Collections.emptyList(); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java deleted file mode 100644 index 656607d..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/BlockTypeConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import com.mcmoddev.orespawn.data.BlockType; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.util.ResourceLocation; - -public class BlockTypeConfig { - public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(BlockType.BlockTypeType.CODEC.fieldOf("type").forGetter((config) -> config.type), - ResourceLocation.CODEC.fieldOf("name").forGetter((config) -> config.name)).apply(base, BlockTypeConfig::new); - }); - - public final BlockType.BlockTypeType type; - public final ResourceLocation name; - - public BlockTypeConfig(final BlockType.BlockTypeType refType, final ResourceLocation refID) { - this.type = refType; - this.name = refID; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java deleted file mode 100644 index 930947d..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DefaultFeatureParametersConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import com.electronwill.nightconfig.core.conversion.InvalidValueException; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; - -public class DefaultFeatureParametersConfig { - public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(Codec.intRange(0, 255).fieldOf("size").forGetter((config) -> config.size), - Codec.intRange(0, 255).fieldOf("minHeight").forGetter((config) -> config.minHeight), - Codec.intRange(0, 255).fieldOf("maxHeight").forGetter((config) -> config.minHeight), - Codec.floatRange(0, 1).fieldOf("frequency").forGetter((config) -> config.frequency)).apply(base, DefaultFeatureParametersConfig::new); - }); - - public final int size; - public final float frequency; - public final int minHeight; - public final int maxHeight; - - public DefaultFeatureParametersConfig(final int size, final int minHeight, final int maxHeight, final float frequency) { - if (minHeight > maxHeight) - throw new InvalidValueException("Minimum Height must be less than or equal to Maximum Height"); - this.size = size; - this.minHeight = minHeight; - this.maxHeight = maxHeight; - this.frequency = frequency; - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java b/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java deleted file mode 100644 index e6cabe9..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/codecs/DimensionMatcherConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mcmoddev.orespawn.utils.codecs; - -import com.google.common.collect.ImmutableList; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.util.ResourceLocation; - -import java.util.Collections; -import java.util.List; - -public class DimensionMatcherConfig { - public final List data; - public final ResourceLocation type; - - public static final Codec CODEC = RecordCodecBuilder.create((base) -> { - return base.group(ResourceLocation.CODEC.listOf().fieldOf("data").forGetter((config) -> { - return config.data; - }), - ResourceLocation.CODEC.fieldOf("type").forGetter((config) -> { - return config.type; - })).apply(base, DimensionMatcherConfig::new); - }); - - public DimensionMatcherConfig(final List entryList, final ResourceLocation type) { - this.data = ImmutableList.copyOf(entryList); - this.type = type; - } - - public DimensionMatcherConfig() { - type = AllowDenyListBase.denyall; - data = Collections.emptyList(); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java deleted file mode 100644 index 34f49db..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/mixins/BiomeGenerationSettingsAccessor.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.mcmoddev.orespawn.utils.mixins; - -import net.minecraft.world.biome.BiomeGenerationSettings; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.List; -import java.util.function.Supplier; - -@Mixin(BiomeGenerationSettings.class) -public interface BiomeGenerationSettingsAccessor { - @Accessor - @Mutable - // List>>> - void setFeatures(List>>> features); -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java b/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java deleted file mode 100644 index 6bb85ea..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/mixins/ServerAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mcmoddev.orespawn.utils.mixins; - -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.registry.DynamicRegistries; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(MinecraftServer.class) -public interface ServerAccessor { - @Accessor - DynamicRegistries.Impl getDynamicRegistries(); -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java b/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java deleted file mode 100644 index 6f12315..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/WorldGenHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mcmoddev.orespawn.world; - -import com.mcmoddev.orespawn.data.Constants; -import com.mcmoddev.orespawn.utils.Helpers; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; -import net.minecraft.util.registry.WorldGenRegistries; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.IFeatureConfig; -import net.minecraftforge.event.world.BiomeLoadingEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; - -@EventBusSubscriber(modid= Constants.MODID) -public class WorldGenHandler { - -/* - @SubscribeEvent - public static void onBiomesLoaded(BiomeLoadingEvent ev) { - } -*/ - public static void doFeatureRegistration() { - - } - - private static ConfiguredFeature register(String key, ConfiguredFeature configuredFeature) { - return Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, Helpers.makeInternalResourceLocation(key), configuredFeature); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java b/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java deleted file mode 100644 index 9b59a1c..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/features/DefaultFeature.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.mcmoddev.orespawn.world.features; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.BiomeMatcher; -import com.mcmoddev.orespawn.data.DimensionMatcher; -import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; -import com.mcmoddev.orespawn.world.gen.configs.OreSpawnFeature; -import com.mojang.serialization.Codec; -import net.minecraft.util.RegistryKey; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ISeedReader; -import net.minecraft.world.World; -import net.minecraft.world.gen.ChunkGenerator; - -import javax.annotation.Nonnull; -import java.util.Random; - -public class DefaultFeature extends OreSpawnFeature { - private BiomeMatcher biomeMatcher; - private DimensionMatcher dimensionMatcher; - - public DefaultFeature(Codec codec) { - super(codec); - } - - public boolean place(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { - OreSpawn.LOGGER.info("Yes, the method needed is called place and not generate, regardless of what IDEA says"); - return this.generate(reader, chunkGenerator, rand, pos, config); - } - - @Override - public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull DefaultFeatureConfig config) { - OreSpawn.LOGGER.info("generate( ... ) for %s", config.feature); - if (biomeMatcher == null) biomeMatcher = new BiomeMatcher(config.biomeMatch); - if (dimensionMatcher == null) dimensionMatcher = new DimensionMatcher(config.dimensionMatch); - if (config.parameters.frequency <= rand.nextFloat()) { - ResourceLocation b = reader.getBiome(pos).getRegistryName(); - if (biomeMatcher.matches(b)) { - RegistryKey dx = reader.getWorld().getDimensionKey();//.getRegistryName(); - ResourceLocation d = dx.getLocation(); - - if (dimensionMatcher.matches(d)) { - return placeFeature(reader, chunkGenerator, pos, config); - } - } - } - return false; - } - - private boolean placeFeature(ISeedReader reader, ChunkGenerator chunkGenerator, BlockPos pos, DefaultFeatureConfig config) { - OreSpawn.LOGGER.info("placeFeature(%s, %s, %s) for %s", reader, chunkGenerator, pos, config.feature); - return true; - } - -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/features/Features.java b/src/main/java/com/mcmoddev/orespawn/world/features/Features.java deleted file mode 100644 index 88e68d6..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/features/Features.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mcmoddev.orespawn.world.features; - -import com.mcmoddev.orespawn.world.gen.configs.DefaultFeatureConfig; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.gen.feature.Feature; -import net.minecraft.world.gen.feature.IFeatureConfig; - -public class Features extends net.minecraftforge.registries.ForgeRegistryEntry> { - public static final DefaultFeature DEFAULT = new DefaultFeature(DefaultFeatureConfig.CODEC); -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java deleted file mode 100644 index dc3722a..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/DefaultFeatureConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.mcmoddev.orespawn.world.gen.configs; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.utils.codecs.BiomeMatcherConfig; -import com.mcmoddev.orespawn.utils.codecs.DefaultFeatureParametersConfig; -import com.mcmoddev.orespawn.utils.codecs.DimensionMatcherConfig; -import com.mcmoddev.orespawn.world.features.DefaultFeature; -import com.mcmoddev.orespawn.world.features.Features; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.block.BlockState; -import net.minecraft.util.RegistryKey; -import net.minecraft.util.WeightedList; -import net.minecraft.util.registry.DynamicRegistries; -import net.minecraft.util.registry.MutableRegistry; -import net.minecraft.util.registry.Registry; -import net.minecraft.util.registry.WorldGenRegistries; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.IFeatureConfig; -import net.minecraft.world.gen.feature.template.IRuleTestType; -import net.minecraft.world.gen.feature.template.RuleTest; - -import java.util.*; -import java.util.stream.Stream; - -public class DefaultFeatureConfig implements IFeatureConfig { - public static final Codec CODEC = RecordCodecBuilder.create((codec) -> { - return codec.group( - Codec.STRING.fieldOf("name").forGetter((config) -> config.feature), - RuleTest.CODEC.fieldOf("replaces").forGetter((config) -> config.replacer), - DefaultFeatureParametersConfig.CODEC.fieldOf("parameters").forGetter((config) -> config.parameters), - BiomeMatcherConfig.CODEC.fieldOf("biomes").forGetter((config) -> config.biomeMatch), - DimensionMatcherConfig.CODEC.fieldOf("dimensions").forGetter((config) -> config.dimensionMatch), - WeightedList.getCodec(BlockState.CODEC).fieldOf("blocks").forGetter((config) -> config.blocks) - ).apply(codec, DefaultFeatureConfig::generateFeature); - }); - - public final WeightedList blocks; - public final BiomeMatcherConfig biomeMatch; - public final DimensionMatcherConfig dimensionMatch; - public final DefaultFeatureParametersConfig parameters; - public final String feature; - public final RuleTest replacer; - - private static final Map> myFeatures = new TreeMap<>(); - - private static DefaultFeatureConfig generateFeature(String featureName, RuleTest replacement, - DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, - DimensionMatcherConfig dimensions, WeightedList blocks ) { - DefaultFeatureConfig z = new DefaultFeatureConfig(featureName, replacement, parameters, biomes, dimensions, blocks); - if (!myFeatures.containsKey(featureName)) { - myFeatures.put(featureName, Features.DEFAULT.withConfiguration(z)); - Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, featureName, Features.DEFAULT.withConfiguration(z)); - } - return z; - } - - public DefaultFeatureConfig(String featureName, RuleTest replacement, - DefaultFeatureParametersConfig parameters, BiomeMatcherConfig biomes, - DimensionMatcherConfig dimensions, WeightedList blocks ) { - this.replacer = replacement; - this.blocks = blocks; - this.biomeMatch = biomes; - this.dimensionMatch = dimensions; - this.parameters = parameters; - this.feature = featureName; - OreSpawn.LOGGER.info("Feature %s configured", featureName); - } - - public Stream> getConfiguredFeatures() { - return myFeatures.values().stream(); - } - - public static Map> getMyFeatures() { - return Collections.unmodifiableMap(myFeatures); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java b/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java deleted file mode 100644 index 49ca0ff..0000000 --- a/src/main/java/com/mcmoddev/orespawn/world/gen/configs/OreSpawnFeature.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mcmoddev.orespawn.world.gen.configs; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mojang.serialization.Codec; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ISeedReader; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.feature.Feature; -import net.minecraft.world.gen.feature.IFeatureConfig; - -import javax.annotation.Nonnull; -import java.util.Random; - -public class OreSpawnFeature extends Feature { - public OreSpawnFeature(Codec configCodec) { - super(configCodec); - } - - @Override - public boolean generate(@Nonnull ISeedReader reader, @Nonnull ChunkGenerator generator, @Nonnull Random rand, @Nonnull BlockPos pos, @Nonnull FC config) { - OreSpawn.LOGGER.fatal("Inconceivably reached lowest level generator setup"); - return false; - } -} From 2beeef78a7faafdf01ce64eb59f5bb422c64b200 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Tue, 29 Jul 2025 12:01:21 -0400 Subject: [PATCH 36/37] actually for 1.20.1, IIRC --- CHANGELOG.txt | 1 - Jenkinsfile | 96 ---- LICENSE | 516 +----------------- README.md | 74 +-- TODO.txt | 5 - build.gradle | 233 ++++---- changelog.mustache | 10 - config/checkstyle/checkstyle-6.xml | 96 ---- config/checkstyle/checkstyle.xml | 96 ---- config/checkstyle/pmd-5.8.xml | 483 ---------------- config/checkstyle/pmd.xml | 334 ------------ config/pmd/pmd-5.1.xml | 468 ---------------- config/pmd/pmd-5.8.xml | 483 ---------------- config/pmd/pmd.xml | 334 ------------ config/xsl/checkstyle-author.xsl | 224 -------- .../checkstyle-noframes-severity-sorted.xsl | 202 ------- config/xsl/checkstyle-noframes-sorted.xsl | 172 ------ config/xsl/checkstyle-noframes.xsl | 171 ------ gradle.properties | 175 ++---- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 285 ++++++---- gradlew.bat | 37 +- settings.gradle | 36 -- sonar-project.properties | 20 - .../java/com/mcmoddev/orespawn/OreSpawn.java | 67 +-- .../com/mcmoddev/orespawn/data/Config.java | 76 --- .../com/mcmoddev/orespawn/data/Constants.java | 37 -- .../com/mcmoddev/orespawn/utils/Loaders.java | 124 ----- src/main/resources/META-INF/mods.toml | 31 -- .../configured_feature/test_entry.json | 20 - src/main/resources/orespawn4.mixins.json | 14 - src/main/resources/pack.mcmeta | 7 - update.json | 80 --- 34 files changed, 475 insertions(+), 4536 deletions(-) delete mode 100644 CHANGELOG.txt delete mode 100644 Jenkinsfile delete mode 100644 TODO.txt delete mode 100644 changelog.mustache delete mode 100644 config/checkstyle/checkstyle-6.xml delete mode 100644 config/checkstyle/checkstyle.xml delete mode 100644 config/checkstyle/pmd-5.8.xml delete mode 100644 config/checkstyle/pmd.xml delete mode 100644 config/pmd/pmd-5.1.xml delete mode 100644 config/pmd/pmd-5.8.xml delete mode 100644 config/pmd/pmd.xml delete mode 100644 config/xsl/checkstyle-author.xsl delete mode 100644 config/xsl/checkstyle-noframes-severity-sorted.xsl delete mode 100644 config/xsl/checkstyle-noframes-sorted.xsl delete mode 100644 config/xsl/checkstyle-noframes.xsl delete mode 100644 settings.gradle delete mode 100644 sonar-project.properties delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/Config.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/data/Constants.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/utils/Loaders.java delete mode 100644 src/main/resources/META-INF/mods.toml delete mode 100644 src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json delete mode 100644 src/main/resources/orespawn4.mixins.json delete mode 100644 src/main/resources/pack.mcmeta delete mode 100644 update.json diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index f5df02e..0000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1 +0,0 @@ -Example Mod \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 76978fe..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,96 +0,0 @@ -pipeline { - agent any - environment { - GRADLE_OPTS = '-Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dorg.gradle.warning.mode=all' -// JAVA_OPTS = '' - } - options { - ansiColor('xterm') - } - tools { -// git 'Git' - gradle 'Gradle 4.9' - jdk 'oraclejdk8' - } - stages { - stage('prebuild') { - steps { - sh 'rm -rf build/libs' - sh 'chmod +x gradlew' - sh 'java -version' - sh 'gradle -version' - sh './gradlew -version' - sh 'export' - } - } - stage('build') { - steps { - withGradle { - sh './gradlew clean build' - } - } - } - stage('test') { - steps { - withGradle { - sh './gradlew test' - } - } - } - stage('publish') { - steps { - withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { - withGradle { - sh './gradlew publish' - } - } - } - } - stage('CurseForge') { - steps { - withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { - withGradle { - sh './gradlew -x publish curseforge' - } - } - } - } - stage('SonarQube') { - tools { - jdk "oraclejdk11" - } - environment { - scannerHome = tool 'SonarQube' - } - steps { -// withCredentials([file(credentialsId: 'secret.json', variable: 'SECRET_FILE')]) { -// withGradle { -// sh './gradlew sonarqube' -// } -// } - withSonarQubeEnv(installationName: 'SonarCloud', , envOnly: false) { - sh "${scannerHome}/bin/sonar-scanner -Dsonar.java.jdkHome=${JAVA_HOME}" - } - } - } - stage('postbuild') { - steps { - archiveArtifacts artifacts: 'build/libs/*.jar', followSymlinks: false - javadoc javadocDir: 'build/docs/javadoc', keepAll: false - fingerprint 'build/libs/*.zip' - junit allowEmptyResults: true, testResults: '**/build/test-results/junit-platform/*.xml' - jacoco classPattern: '**/build/classes/java', execPattern: '**/build/jacoco/**.exec', sourceInclusionPattern: '**/*.java', sourcePattern: '**/src/main/java' - findBuildScans() -// recordIssues(tools: [errorProne(pattern: 'ReportFilePattern', reportEncoding: 'UTF-8')]) - recordIssues(tools: [java()]) - recordIssues(tools: [javaDoc()]) - recordIssues(tools: [checkStyle(pattern: '**/build/reports/checkstyle/*.xml')]) - recordIssues(tools: [pmdParser(pattern: '**/build/reports/pmd/*.xml')]) -// recordIssues(tools: [findBugs(pattern: '*/build/reports/findbugs/*.xml', useRankAsPriority: true)]) - recordIssues(tools: [spotBugs(pattern: '**/build/reports/spotbugs/*.xml', useRankAsPriority: true)]) - recordIssues(tools: [junitParser(pattern: '**/build/test-results/junit-platform/*.xml')]) -// recordIssues(tools: [sonarQube(pattern: '**/sonar-report.json')]) - } - } - } -} diff --git a/LICENSE b/LICENSE index 19e3071..b64bc64 100644 --- a/LICENSE +++ b/LICENSE @@ -1,504 +1,24 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 +MIT License - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +Copyright (c) 2023 NeoForged project -(This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.) +This license applies to the template files as supplied by github.com/NeoForged/MDK - Preamble - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random - Hacker. - - {signature of Ty Coon}, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b0cdaaa..31baeae 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,45 @@ -# ForgeModdingSkeleton -Skeletons for building Forge mods +# MMD OreSpawn 4.x +A re-imagining of an old MMD Mod for modern Minecraft. Now we just provide the feature placers and let the existing +datapack infrastruture handle the rest. -## TODO -1) Support Curse ChangeLog from String (Partially done) -2) Sonarqube & Jar Signing Support for Travis -3) Sonarqube & Jar Signing Support for CircleCI -4) Secret parser should support Gradle properties too -5) Fix JUnit3, JUnit4 & JUnit Vintage compatibility (Should be done) -6) Support for JUnit on gradle < 4.6 (Effectively done, Needs uncommented and tested) -7) Jar-in-Jar (Partially done) -8) ContainedDeps (Partially done) -9) Shade -10) Shadow -11) Make all gradle version specific bits gated (Mostly complete) -12) Auto-selection selection of FG version based on MC version -13) Auto-selection of Forge latest LB and RB versions -14) Auto-selection of Mappings +Work Started: 19-AUG-2024 D. Hazelton +Beta Work Completed: 29-JUL-2025 D. Hazelton -## Known Caveats -1) Many versions hard-coded -2) Only supports the maven-publish plugin, not the old maven plugin which also supports SSH Pubkey authentication +## How it works +This version of MMD OreSpawn adds a set of generators that can be used similar to the vanilla ones. In fact you use +them exactly the same, by putting the right ID in the right JSON file in a data pack. The following example replaced +the standard coal spawn as part of testing the code and uses the `vein` feature generator: +```json +{ + "type": "mmdorespawn:mmdos4_vein", + "config": { + "discard_chance_on_air_exposure": 0.5, + "size": 17, + "min_length": 100, + "max_length": 200, + "frequency": 1.0, + "targets": [ + { + "state": { + "Name": "minecraft:coal_ore" + }, + "target": { + "predicate_type": "minecraft:tag_match", + "tag": "minecraft:stone_ore_replaceables" + } + }, + { + "state": { + "Name": "minecraft:deepslate_coal_ore" + }, + "target": { + "predicate_type": "minecraft:tag_match", + "tag": "minecraft:deepslate_ore_replaceables" + } + } + ] + } +} +``` -## Script related TODO -1) Document secret.json (Partially done) -2) Finish off JUnit5 feature set (I think this is done now) -3) Make sure JaCoCo is integrated correctly with JUnit -## Example Mod TODO -1) Recipes -2) Creative Tabs -3) Block with Multiple Textures -4) Entities -5) Networking -6) Capabilities -7) Multiblock structures -8) Worldgen: Ore and Structure generation diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index b945ff3..0000000 --- a/TODO.txt +++ /dev/null @@ -1,5 +0,0 @@ -1) Toss entire JSON loading and data management system -2) Implement BiomeMatcher, OS4BlockMatcher and DimensionMatcher with codecs -3) Each Feature gets a Codec and Configuration unique to it -4) Register IFeature and IFeatureConfiguration stuff -5) Fully implement all available features diff --git a/build.gradle b/build.gradle index 4a22ccd..1d03b08 100644 --- a/build.gradle +++ b/build.gradle @@ -1,119 +1,168 @@ -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' } - mavenCentral() - maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } - } - dependencies { - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true - classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT' - } -} - plugins { - id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.66' - id 'eclipse' - id 'idea' - id 'maven-publish' + id 'java-library' + id 'eclipse' + id 'idea' + id 'maven-publish' + id 'net.neoforged.gradle.userdev' version '7.0.142' } -apply plugin: 'net.minecraftforge.gradle' -apply plugin: 'org.spongepowered.mixin' -mixin { - add sourceSets.main, "orespawn4.refmap.json" -} - -project.ext.short_version = '4.0.0' -version = (project.hasProperty('mc_version') ? project.mc_version : project.forge_mc_version) + '-' + short_version -project.ext.display_version = short_version -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 -compileJava.sourceCompatibility = JavaVersion.VERSION_1_8 -compileJava.targetCompatibility = JavaVersion.VERSION_1_8 +version = mod_version +group = mod_group_id -eclipse.project { - buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' - natures 'org.eclipse.buildship.core.gradleprojectnature' +repositories { + mavenLocal() + maven { + url = 'https://maven.blamejared.com' + name = 'BlameJared Maven' + } } -idea.module { - downloadJavadoc = true +base { + archivesName = mod_id } -minecraft { - mappings channel: project.mcp_mappings_channel, version: project.mcp_mappings_version +// Mojang ships Java 21 to end users starting in 1.20.5, so mods should target Java 21. +java.toolchain.languageVersion = JavaLanguageVersion.of(21) + +//minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') +//minecraft.accessTransformers.entry public net.minecraft.client.Minecraft textureManager # textureManager + +// Default run configurations. +// These can be tweaked, removed, or duplicated as needed. +runs { + // applies to all the run configs below + configureEach { + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + systemProperty 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + systemProperty 'forge.logging.console.level', 'debug' + + modSource project.sourceSets.main + } - runs { client { - workingDirectory project.file('run') - arg '-mixin.config=orespawn4.mixins.json' - taskName 'runClient' - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - - mods { - orespawn4 { - source sourceSets.main - } - } + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id } server { - workingDirectory project.file('run') - arg '-mixin.config=orespawn4.mixins.json' + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id + programArgument '--nogui' + } - taskName 'runServer' - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + systemProperty 'forge.enabledGameTestNamespaces', project.mod_id + } - mods { - orespawn4 { - source sourceSets.main - } - } + data { + // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it + // workingDirectory project.file('run-data') + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() } +} - data { - workingDirectory project.file('run') +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - args '--mod', 'orespawn', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/'), '-mixin.config', 'orespawn4.mixins.json' +// Sets up a dependency configuration called 'localRuntime'. +// This configuration should be used instead of 'runtimeOnly' to declare +// a dependency that will be present for runtime testing but that is +// "optional", meaning it will not be pulled by dependents of this mod. +configurations { + runtimeClasspath.extendsFrom localRuntime +} - mods { - orespawn4 { - source sourceSets.main - } - } +dependencies { + // Specify the version of Minecraft to use. + // Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above. + // The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version. + // You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader. + // And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version. + // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. + implementation "net.neoforged:neoforge:${neo_version}" + compileOnly "com.blamejared.crafttweaker:CraftTweaker-neoforge-${minecraft_version}:${crafttweaker_version}" + // Example optional mod dependency with JEI + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" + // compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}" + // We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it + // localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}" + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation "blank:coolmod-${mc_version}:${coolmod_version}" + + // Example mod dependency using a file as dependency + // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") + + // Example project dependency using a sister or child project: + // implementation project(":myproject") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html +} +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. +// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html +tasks.withType(ProcessResources).configureEach { + var replaceProperties = [ + minecraft_version : minecraft_version, + minecraft_version_range: minecraft_version_range, + neo_version : neo_version, + neo_version_range : neo_version_range, + loader_version_range : loader_version_range, + mod_id : mod_id, + mod_name : mod_name, + mod_license : mod_license, + mod_version : mod_version, + mod_authors : mod_authors, + mod_description : mod_description + ] + inputs.properties replaceProperties + + filesMatching(['META-INF/neoforge.mods.toml']) { + expand replaceProperties } - } } -dependencies { - implementation 'org.jetbrains:annotations:20.1.0' ; - minecraft "net.minecraftforge:forge:${project.forge_mc_version}-${project.forge_version}" ; - annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' ; - annotationProcessor 'org.spongepowered:mixin:0.8.4:processor' ; +// Example configuration to allow publishing using the maven-publish plugin +publishing { + publications { + register('mavenJava', MavenPublication) { + from components.java + } + } + repositories { + maven { + url "file://${project.projectDir}/repo" + } + } } -jar { - manifest { - attributes([ - 'Maven-Artifact' : "${project.group}:${project.archivesBaseName}:${project.version}", - 'Timestamp' : System.currentTimeMillis(), - 'Specification-Title' : project.modid, - 'Specification-Vendor' : project.vendor, - 'Specification-Version' : '1', - 'Implementation-Title' : project.archivesBaseName, - 'Implementation-Version' : project.version, - 'Implementation-Vendor' : project.vendor, - 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Built-On-Java' : "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On' : "${project.forge_mc_version}-${project.forge_version}", - 'MixinConfigs' : "orespawn4.mixins.json" - ]) - } +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation +} + +// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. +idea { + module { + downloadSources = true + downloadJavadoc = true + } } diff --git a/changelog.mustache b/changelog.mustache deleted file mode 100644 index 076ba59..0000000 --- a/changelog.mustache +++ /dev/null @@ -1,10 +0,0 @@ -{{#tags}} - {{#issues}} - {{#commits}} -**{{{messageTitle}}}** -{{#messageBodyItems}} - * {{.}} -{{/messageBodyItems}} - {{/commits}} - {{/issues}} -{{/tags}} diff --git a/config/checkstyle/checkstyle-6.xml b/config/checkstyle/checkstyle-6.xml deleted file mode 100644 index 589a7e4..0000000 --- a/config/checkstyle/checkstyle-6.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index 55c1473..0000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/checkstyle/pmd-5.8.xml b/config/checkstyle/pmd-5.8.xml deleted file mode 100644 index 525914a..0000000 --- a/config/checkstyle/pmd-5.8.xml +++ /dev/null @@ -1,483 +0,0 @@ - - - - - PMD Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/checkstyle/pmd.xml b/config/checkstyle/pmd.xml deleted file mode 100644 index 7dda2be..0000000 --- a/config/checkstyle/pmd.xml +++ /dev/null @@ -1,334 +0,0 @@ - - - - - PMD Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/pmd/pmd-5.1.xml b/config/pmd/pmd-5.1.xml deleted file mode 100644 index 907dc94..0000000 --- a/config/pmd/pmd-5.1.xml +++ /dev/null @@ -1,468 +0,0 @@ - - - - - PMD Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/pmd/pmd-5.8.xml b/config/pmd/pmd-5.8.xml deleted file mode 100644 index 525914a..0000000 --- a/config/pmd/pmd-5.8.xml +++ /dev/null @@ -1,483 +0,0 @@ - - - - - PMD Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/pmd/pmd.xml b/config/pmd/pmd.xml deleted file mode 100644 index 7dda2be..0000000 --- a/config/pmd/pmd.xml +++ /dev/null @@ -1,334 +0,0 @@ - - - - - PMD Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/xsl/checkstyle-author.xsl b/config/xsl/checkstyle-author.xsl deleted file mode 100644 index a924bb1..0000000 --- a/config/xsl/checkstyle-author.xsl +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - -
- - - - - - - - - -
- - -

CheckStyle Audit

-
Designed for use with CheckStyle and Ant.
-
- - - -
- - - -
- - - - - -

-

- -


- - - - -

Summary

- - - - - - - - - - - - - - - -
FilesErrorsWarnings
- - - - - -
-
- -

Authors

- - - - - - - - - - - - - - - - - - - - - - - - -
NameErrorsWarnings
- - - -
-
- - - - - - - -
-

File -
- Author -

- - - - - - - - - - - - - -
Error DescriptionLine
- - - -
-
Back to top - - - - - - - - - substring-after($path, '\') - - - - - - - - - - ab - - diff --git a/config/xsl/checkstyle-noframes-severity-sorted.xsl b/config/xsl/checkstyle-noframes-severity-sorted.xsl deleted file mode 100644 index e83f50d..0000000 --- a/config/xsl/checkstyle-noframes-severity-sorted.xsl +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

CheckStyle Audit

Designed for use with CheckStyle and Ant.
-
- - - -
- - - -
- - - - -
- - -
- - -

Files

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameErrorsWarningsInfos
-
- - - -

File

- - - - - - - - - - - - - - - - -
SeverityError DescriptionLine
- Back to top -
- - -

Summary

- - - - - - - - - - - - - - - - - - -
FilesErrorsWarningsInfos
-
- - - - a - b - - -
- - diff --git a/config/xsl/checkstyle-noframes-sorted.xsl b/config/xsl/checkstyle-noframes-sorted.xsl deleted file mode 100644 index dc6486e..0000000 --- a/config/xsl/checkstyle-noframes-sorted.xsl +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

CheckStyle Audit

Designed for use with CheckStyle and Ant.
-
- - - -
- - - -
- - - - -
- - -
- - -

Files

- - - - - - - - - - - - - - - -
NameErrors
-
- - - -

File

- - - - - - - - - - - - - - -
Error DescriptionLine
- Back to top -
- - -

Summary

- - - - - - - - - - - - -
FilesErrors
-
- - - - a - b - - -
- - diff --git a/config/xsl/checkstyle-noframes.xsl b/config/xsl/checkstyle-noframes.xsl deleted file mode 100644 index 576a809..0000000 --- a/config/xsl/checkstyle-noframes.xsl +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

CheckStyle Audit

Designed for use with CheckStyle and Ant.
-
- - - -
- - - -
- - - - - -

-

- - -


- - -
- - -

Files

- - - - - - - - - - - - - - -
NameErrors
-
- - - -

File

- - - - - - - - - - - - - -
Error DescriptionLine
- Back to top -
- - -

Summary

- - - - - - - - - - - - -
FilesErrors
-
- - - - a - b - - -
diff --git a/gradle.properties b/gradle.properties index 873404e..4394fab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,130 +1,45 @@ -org.gradle.daemon = false - org.gradle.jvmargs=-Xmx6144M -group = com.mcmoddev -archivesBaseName = OreSpawn -modid = orespawn -vendor = mcmoddev -name = MMD OreSpawn -description = Spawn Your ORes! - -#Minumum Minecraft Version Mod will be built with -mc_version=1.16.5 - -#ForgeGradle version to use (Required) -fg_version = 5.+ -#version of Minecraft Forge is built against (Required) -forge_mc_version = 1.16.5 -#Forge version to use (Required) -forge_version = 36.1.32 -#Mappings channel: snapshot, stable or official -mcp_mappings_channel = snapshot -#Mappings version to use (Required) -mcp_mappings_version = 20210309-1.16.5 - -# The following will currently only be set when using Gradle 4.8 or later -pom_information = true -pom_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn' - -pom_license_information = true -pom_license_name = 'GNU Lesser General Public License v2.1' -pom_license_url = 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt' -pom_comments = '' - -pom_scm_information = true -pom_scm_connection = 'scm:git:git://github.com/MinecraftModDevelopmentMods/OreSpawn.git' -pom_scm_developer_connection = 'scm:git:git@github.com:MinecraftModDevelopmentMods/OreSpawn.git' -pom_scm_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn' - -pom_issue_information = true -pom_issue_system = 'github' -pom_issue_url = 'https://github.com/MinecraftModDevelopmentMods/OreSpawn/issues' - -pom_ci_information = true -pom_ci_system = 'jenkins' -pom_ci_url = 'https://ci.mcmoddev.com/' - -pom_organisation_information = true -pom_organization_name = 'Minecraft Mod Development' -pom_organization_url = 'https://mcmoddev.com/' - -#curseforge_do_upload=false -#curseforge_debug=false -#Cursforge Project ID (Required) -#curseforge_project_id = 0 -#Type of Release, can be one of 'alpha', 'beta' or 'release' (Defaults to 'alpha' if not set) -#curseforge_release_type = alpha -#When using a ChangeLog file specifies filename -#curseforge_changelog_filename = CHANGELOG.txt -#Changelog type, can be one of 'text', 'html' or 'markdown' (Defaults to 'text' if not set) -#curseforge_changelog_type = text -#Version(s) of Minecraft this mod will work on (comma separated list) -#curseforge_versions = 1.16.5 -#Whether to use a custom display name on artifacts -#curseforge_use_custom_display_name = true -#List of required dependencies (comma separated list) -#curseforge_requirements = -#List of optional dependencies (comma separated list) -#curseforge_optionals = -#List of embedded dependencies (comma separated list) -#curseforge_embeddeds = -#List of compatible tools (comma separated list) -#curseforge_tools = -#List of incompatible dependencies (comma separated list) -#curseforge_incompatibles = -#List of included dependencies (comma separated list) -#curseforge_includes = - -#String reference to Core Plugin this mod contains, if any -#core_plugin = -#Whether or not to use Access Transformers from depended mods -#dep_has_ats = false -#File name of this mod's Access Transformers (If any) -#mod_at_file = _at.cfg - -#Whether the artifacts should be signed -do_sign_jar = false - -#Create a source jar? -create_source_jar = true -#Create an API jar? -create_api_jar = false -#Create a deobf jar? -create_deobf_jar = false -#Create a javadoc jar? -create_javadoc_jar = true - -#The following four options are optional and do not affect your build in any way -#They are only for convenience when using the 'runClient' and 'runServer' tasks -#Whether to Disable server gui -#mc_server_nogui = true -#Whether to Skip the screen to confirm that you want to load a world with missing registry entries -#forge_do_not_backup = true -#Whether to skip having to confirm on server -#forge_query_result_confirm = true -#Whether to Skip jansi warnings in the log -#log4j_skip_jansi = true - -# Optional convenience setter for game resolution one of '480p', '576p', '720p', '1080p' or 'custom' -#mc_resolution = 1080p -#the following two will only be used when mc_resolution = custom -#mc_custom_resolution_width = 1920 -#mc_custom_resolution_height = 1080 -#mc_fullscreen = true - -#Additional arguments to pass to minecraft.clientJvmArgs -#client_jvm_args = -#Additional arguments to pass to minecraft.serverJvmArgs -#client_game_args = -#Additional arguments to pass to minecraft.clientRunArgs -#server_jvm_args = -#Additional arguments to pass to minecraft.serverRunArgs -#server_game_args = - -##The following three options should *NEVER* be set in this file, it's a security risk, Be safe, keep your account information private. -##These properties here are for reference only and explanation of what they do -##Your Minecraft account username (Consistent player name for singleplayer worlds) -##mc_username = -##Your Minecraft account password (Allows online play while present with associated username) -##mc_password = -##Your Minecraft account uuid (Allows using your skin while testing, This can be with or without hyphens) -##mc_uuid = +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx1G +org.gradle.daemon=false +org.gradle.debug=false + +#read more on this at https://github.com/neoforged/NeoGradle/blob/NG_7.0/README.md#apply-parchment-mappings +# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started +neogradle.subsystems.parchment.minecraftVersion=1.20.6 +neogradle.subsystems.parchment.mappingsVersion=2024.06.16 +# Environment Properties +# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge +# The Minecraft version must agree with the Neo version to get a valid artifact +minecraft_version=1.20.6 +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[1.20.6,1.21) +# The Neo version must agree with the Minecraft version to get a valid artifact +neo_version=20.6.119 +# The Neo version range can use any version of Neo as bounds +neo_version_range=[20.6,) +# The loader version range can only use the major version of FML as bounds +loader_version_range=[2,) + +## Mod Properties + +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=mmdorespawn +# The human-readable display name for the mod. +mod_name=MMD OreSpawn +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=All Rights Reserved +# The mod version. See https://semver.org/ +mod_version=4.0.0 +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=com.mcmoddev.orespawn +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors=DShadowWolf, jriwanek, Cyanobacterium +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description=Continuation (rewrite) of an old 1.12.2 mod, implements a batch of feature providers + +crafttweaker_version=19.0.12 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch literal 43504 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 TZiJi*F`$tkWLn) literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..cea7a79 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..9b42019 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 93a3a1e..0000000 --- a/settings.gradle +++ /dev/null @@ -1,36 +0,0 @@ -// Gradle 6.x or later -plugins { - id 'com.gradle.enterprise' version '3.6.3' -} - -// Workaround to make the JUnit Platform Gradle Plugin available using the `plugins` DSL -// See https://github.com/junit-team/junit5/issues/768 -if (!(GradleVersion.current() <= GradleVersion.version('2.14.1'))) { - pluginManagement { - repositories { - gradlePluginPortal() - maven { url = ArtifactRepositoryContainer.MAVEN_CENTRAL_URL } - } - resolutionStrategy { - eachPlugin { - if (requested.id.id == 'org.junit.platform.gradle.plugin') { - useModule("org.junit.platform:junit-platform-gradle-plugin:${requested.version}") - } - } - } - } -} - -rootProject.name = archivesBaseName -import org.gradle.util.GradleVersion -if ((GradleVersion.current() >= GradleVersion.version('4.8')) && (GradleVersion.current() <= GradleVersion.version('4.10.3'))) { - enableFeaturePreview('STABLE_PUBLISHING') // 4.10.3 -} - -// TODO: Things which likely don't work with FG yet, but I've not checked. -/* -if ((GradleVersion.current() >= GradleVersion.version('4.6')) && (GradleVersion.current() <= GradleVersion.version('4.10.3'))) { - enableFeaturePreview('IMPROVED_POM_SUPPORT') // Previously -Dorg.gradle.advancedpomsupport=true - enableFeaturePreview('GRADLE_METADATA') // Previously -Dorg.gradle.gradlemetadata=true -} -*/ diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index b6ca5fe..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,20 +0,0 @@ -sonar.projectKey=jriwanek_ForgeModdingSkeleton -sonar.organization=jriwanek-github - -# This is the name and version displayed in the SonarCloud UI. -#sonar.projectName=ForgeModdingSkeleton -#sonar.projectVersion=1.0 - -sonar.java.binaries=./build/classes - -# Encoding of the source code. Default is default system encoding -sonar.sourceEncoding=UTF-8 - -#sonar.coverageReportPaths= -#sonar.coverage.jacoco.xmlReportPaths= -#sonar.testExecutionReportPath= -sonar.junit.reportPaths=**/build/test-results/junit-platform/*.xml - -sonar.java.checkstyle.reportPaths=**/build/reports/checkstyle/*.xml -sonar.java.pmd.reportPaths=**/build/reports/pmd/*.xml -sonar.java.spotbugs.reportPaths=**/build/reports/spotbugs/*.xml diff --git a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java index 46e657d..f19a48b 100644 --- a/src/main/java/com/mcmoddev/orespawn/OreSpawn.java +++ b/src/main/java/com/mcmoddev/orespawn/OreSpawn.java @@ -1,42 +1,35 @@ - package com.mcmoddev.orespawn; +package com.mcmoddev.orespawn; - import com.mcmoddev.orespawn.data.Config; - import com.mcmoddev.orespawn.utils.Loaders; - import net.minecraftforge.fml.common.Mod; - import net.minecraftforge.fml.config.ModConfig; - import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; - import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import com.mcmoddev.orespawn.features.NormalCloud; +import com.mcmoddev.orespawn.features.VeinFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import org.slf4j.Logger; - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; +import com.mojang.logging.LogUtils; - @Mod("orespawn") - public class OreSpawn { - // Directly reference a log4j logger. - public static final Logger LOGGER = LogManager.getFormatterLogger(); +import net.minecraft.core.registries.BuiltInRegistries; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; - public OreSpawn() { - // Register the setup method for modloading - // find and load the configs - for this we just use a custom class... -// Loaders.loadConfigs(); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); -// FMLJavaModLoadingContext.get().getModEventBus().addListener(this::modConfig); - // register features so they can be used elsewhere - Features.loadAndRegister(); - } +// The value here should match an entry in the META-INF/neoforge.mods.toml file +@Mod(OreSpawn.MODID) +public class OreSpawn +{ + // Define mod id in a common place for everything to reference + public static final String MODID = "mmdorespawn"; + // Directly reference a slf4j logger + public static final Logger LOGGER = LogUtils.getLogger(); + public static final DeferredRegister> FEATURE = DeferredRegister.create(BuiltInRegistries.FEATURE, MODID); + public static final DeferredHolder, Feature> VEIN_FEATURE = FEATURE.register("mmdos4_vein", VeinFeature::new); + public static final DeferredHolder, Feature> NORMAL_CLOUD_FEATURE = FEATURE.register("mmdos4_normal_cloud", NormalCloud::new); + public static final DeferredHolder, Feature> CLUSTERS_FEATURE = FEATURE.register("mmdos4_clusters", NormalCloud::new); -/* - public void modConfig(ModConfig.ModConfigEvent event) - { - ModConfig config = event.getConfig(); - Config.refresh(); - } -*/ - - private void setup(final FMLCommonSetupEvent event) { - } - - @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) - public static class RegistryEvents { - } - } + // The constructor for the mod class is the first code that is run when your mod is loaded. + // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. + public OreSpawn(IEventBus modEventBus, ModContainer modContainer) { + FEATURE.register(modEventBus); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Config.java b/src/main/java/com/mcmoddev/orespawn/data/Config.java deleted file mode 100644 index 2376f44..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/Config.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import net.minecraftforge.common.ForgeConfigSpec; -import org.apache.commons.lang3.tuple.Pair; - -public class Config { - static - { - final Pair specPair = new ForgeConfigSpec.Builder().configure(CommonConfig::new); - COMMON_SPEC = specPair.getRight(); - COMMON = specPair.getLeft(); - } - - public static final ForgeConfigSpec COMMON_SPEC; - public static final CommonConfig COMMON; - - public static void refresh() { - } - - public static class CommonConfig { - public final ForgeConfigSpec.BooleanValue replaceVanillaOreGeneration; - public final ForgeConfigSpec.BooleanValue replaceAllGeneration; - public final ForgeConfigSpec.BooleanValue enableRetrogeneration; - public final ForgeConfigSpec.BooleanValue forceEnableRetrogeneration; - public final ForgeConfigSpec.BooleanValue flattenBedrock; - public final ForgeConfigSpec.BooleanValue retroactivelyFlattenBedrock; - public final ForgeConfigSpec.IntValue layersOfBedrock; - public final ForgeConfigSpec.BooleanValue extractToDisk; - public final ForgeConfigSpec.BooleanValue ignoreResources; - public final ForgeConfigSpec.BooleanValue ignoreDisk; - - CommonConfig(ForgeConfigSpec.Builder builder) { - builder.push("general"); - replaceVanillaOreGeneration = builder - .comment("Attempt to override vanilla Minecraft ore generation when TRUE") - .translation("text.mmd_orespawn.config.replace_vanilla") - .define("Replace Vanilla Oregen", false); - replaceAllGeneration = builder - .comment("Attempt to replace all ore generation, even from other mods, when TRUE") - .translation("text.mmd_orespawn.config.replace_all") - .define("Replace All Generation", false); - enableRetrogeneration = builder - .comment("Attempt to generate new spawns in chunks that were previously generated or were generated with different configuration options when TRUE") - .translation("text.mmd_orespawn.config.retrogen") - .define("Retrogen", false); - forceEnableRetrogeneration = builder - .comment("Force retroactive generation of new spawns, even if the feature is configured to not perform it when TRUE") - .translation("text.mmd_orespawn.config.force_retrogen") - .define("Force Retrogen", false); - flattenBedrock = builder - .comment("Make the bedrock flat in chunks generated when this option is TRUE") - .translation("text.mmd_orespawn.config.flatten_bedrock") - .define("Flatten Bedrock", true); - layersOfBedrock = builder - .comment("How many layers of Bedrock should there be at the bottom of the world? (default 1, max 4)") - .translation("text.mmd_orespawn.config.bedrock_layers") - .defineInRange("Bedrock Thickness", 1, 1, 4); - retroactivelyFlattenBedrock = builder - .comment("Attempt flatten the bedrock in chunks generated before this option and the \"Flatten Bedrock\" option were set to TRUE") - .translation("text.mmd_orespawn.config.retro_bedrock") - .define("Retrogen Flat Bedrock", false); - extractToDisk = builder - .comment("Extract all integration configurations found to \"config/mmd-orespawn-4\" when TRUE") - .translation("text.mmd_orespawn.config.extract_integration") - .define("Extract Files", false); - ignoreResources = builder - .comment("Do not attempt to locate or use any integration configurations when TRUE") - .translation("text.mmd_orespawn.config.ignore_integration") - .define("Ignore Integration", false); - ignoreDisk = builder - .comment("Do not attempt to load any config files that are on disk when TRUE") - .translation("text.mmd_orespawn.config.ignore_disk") - .define("Ignore Config Files On Disk", false); - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/data/Constants.java b/src/main/java/com/mcmoddev/orespawn/data/Constants.java deleted file mode 100644 index cb24c49..0000000 --- a/src/main/java/com/mcmoddev/orespawn/data/Constants.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.mcmoddev.orespawn.data; - -import java.nio.file.Path; -import java.nio.file.Paths; - -public class Constants { - public static final String VERSION = "4.0.0"; - public static final String CRASH_SECTION = "OreSpawn Version"; - public static final Path SYSCONF = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4, FileBits.SYSCONF); - public static final Path JSONPATH = Paths.get(FileBits.CONFIG_DIR, FileBits.OS4); - public static class ConfigNames { - - public static final String REPLACEMENT = "replaces"; - public static final String FEATURE = "feature"; - public static final String DIMENSIONS = "dimensions"; - public static final String BIOMES = "biomes"; - public static final String BLOCKS = "blocks"; - public static final String PARAMETERS = "parameters"; - public static final String VERSION = "version"; - public static final String SPAWNS = "spawns"; - public static final String ENABLED = "enabled"; - public static final String RETROGEN = "retrogen"; - } - public static class FileBits { - public static final String CONFIG_DIR = "config"; - public static final String OS4 = "mmd-orespawn-4"; - public static final String SYSCONF = "sysconf"; - public static final String PRESETS = "presets.json"; - public static final String ALLOWED_MODS = "active_mods.json"; - public static final String DISK = "__DISK__"; - public static final String RESOURCE = "__RESOURCE__"; - public static final String RESOURCE_PATH = "/assets/orespawn-data"; - } - public enum FileTypes { - FEATURES, SPAWN, PRESETS, REPLACEMENTS - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java b/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java deleted file mode 100644 index d129df4..0000000 --- a/src/main/java/com/mcmoddev/orespawn/utils/Loaders.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.mcmoddev.orespawn.utils; - -import com.google.common.base.Joiner; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.stream.JsonReader; - -import com.mcmoddev.orespawn.OreSpawn; -import com.mcmoddev.orespawn.data.Constants; - -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; - -import org.apache.commons.io.FileUtils; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import static com.mcmoddev.orespawn.data.Config.COMMON; - -public class Loaders { - protected static List foundFiles = new LinkedList<>(); - private static Map loadedConfigs = new ConcurrentHashMap<>(); - - public static void loadConfigs() { - ModList.get().getModFiles().stream().map(mfi -> iterateFiles(mfi.getFile())) - .forEach(foundFiles::addAll); - iterDisk().stream().forEach(foundFiles::add); - // okay, at this point we have things found, but not loaded - first we need a filter of the thing - // based on existing configs - // TODO: Custom config system needed here - this is before the Forge config stuff is ready - // TODO: Custom Config should be an extended JSON - see about doing it like some older stuff written in NodeJS - // and possibly some custom filters that can take basic expressions - if (COMMON.ignoreResources.get()) - foundFiles = foundFiles.stream().filter( rl -> rl.getNamespace() == "orespawn").collect(Collectors.toList()); - if (COMMON.ignoreDisk.get()) - foundFiles = foundFiles.stream().filter( rl -> rl.getNamespace() != "orespawn").collect(Collectors.toList()); - if (!foundFiles.isEmpty()) { - foundFiles = foundFiles.stream().filter( Loaders::runConfiguredFilters ).collect(Collectors.toList()); - } - // now that filtering is done, we can actually loop and load as needed - doActualLoad(); - } - - private static void doActualLoad() { - // doing this as a parallel run and using the 'ifAbsent' form to try and add some speed to the process - foundFiles.parallelStream().forEach( rl -> { - if (rl.getNamespace() == "orespawn") // on disk file - loadedConfigs.putIfAbsent(rl, loadFromDisk(rl)); - else // assume a resource from a mod - loadedConfigs.putIfAbsent(rl, loadAsResource(rl)); - }); - } - - private static JsonObject loadFromDisk(final ResourceLocation loc) { - Path p = Constants.JSONPATH.resolve(String.format("{}.json", loc.getPath())); - JsonParser parser = new JsonParser(); - try (FileInputStream baseInput = new FileInputStream(p.toFile()); - BufferedInputStream dataInput = new BufferedInputStream(baseInput); - InputStreamReader theReader = new InputStreamReader(dataInput)) { - return parser.parse(new JsonReader(theReader)).getAsJsonObject(); - } catch(IOException ex) { - OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); - ex.printStackTrace(); - return new JsonObject(); - } - } - - private static JsonObject loadAsResource(final ResourceLocation loc) { - ModFile mf = ModList.get().getModFileById(loc.getNamespace()).getFile(); - Path p = mf.getLocator().findPath(mf, "assets", "orespawn4-data", loc.getPath()+".json"); - try { - String fileData = FileUtils.readFileToString(p.toFile(), "UTF-8"); - JsonParser parser = new JsonParser(); - return parser.parse(fileData).getAsJsonObject(); - } catch(IOException ex) { - OreSpawn.LOGGER.error("Unable to load known configs file: {}", ex.getMessage()); - ex.printStackTrace(); - return new JsonObject(); - } - } - - private static boolean runConfiguredFilters(ResourceLocation resourceLocation) { - // TODO: Custom Config filters here - // TODO: Basic Whitelist/Blacklist here - return true; // placeholder/stub return - } - - private static List iterDisk() { - try { - return Files.walk(Constants.JSONPATH) - .map( path -> Constants.JSONPATH.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64) // Make sure the depth is within bounds - .filter( path -> path.toString().endsWith(".json")) - .filter( path -> !path.toString().contains(Constants.SYSCONF.toString())) - .map( path -> Joiner.on('/').join(path)) - .map( path -> new ResourceLocation("orespawn", path)) - .collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } - - private static List iterateFiles(ModFile modFile) { - try { - Path root = modFile.getLocator().findPath(modFile, Constants.FileBits.RESOURCE_PATH).toAbsolutePath(); - - return Files.walk(root).map(path -> root.relativize(path.toAbsolutePath())) - .filter(path -> path.getNameCount() <= 64) // Make sure the depth is within bounds - .filter(path -> path.toString().endsWith(".json")) // check extension - .map(path -> Joiner.on('/').join(path)) - .map(path -> path.substring(0, path.length() - 5)) - .map(path -> new ResourceLocation(modFile.getModInfos().get(0).getModId(), path)) - .collect(Collectors.toList()); - } catch (IOException e) { - return Collections.emptyList(); - } - } -} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml deleted file mode 100644 index f8ebc59..0000000 --- a/src/main/resources/META-INF/mods.toml +++ /dev/null @@ -1,31 +0,0 @@ -modLoader="javafml" #mandatory -loaderVersion="[36,)" #mandatory (36 is current forge version) -issueTrackerURL="https://github.com/MinecraftModDevelopmentMods/OreSpawn/issues/" #optional -# GNU LGPLv2.1 -license="GNU LGPLv2.1" -[[mods]] #mandatory -modId="orespawn4" #mandatory -version="${file.jarVersion}" #mandatory -displayName="MMD OreSpawn" #mandatory -updateJSONURL="" # blank for now! -displayURL="https://github.com/MinecraftModDevelopmentMods/OreSpawn/" -logoFile="orespawn.png" #optional -authors="Dr. Plantabyte (aka: Cyanobacterium, Dr. Cyano), Jriwanek, DShadowWolf, others" #optional -# The description text for the mod (multi line!) (#mandatory) -description=''' -A Mod for generating ore deposits in the world. Does not contain dragon girlfriends, mobs or anything like that -''' -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.orespawn]] #optional - modId="forge" #mandatory - mandatory=true #mandatory - versionRange="[36,)" #mandatory - ordering="NONE" - side="BOTH" - -[[dependencies.orespawn]] - modId="minecraft" - mandatory=true - versionRange="[1.16,)" - ordering="NONE" - side="BOTH" diff --git a/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json b/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json deleted file mode 100644 index 96b1480..0000000 --- a/src/main/resources/data/orespawn4/worldgen/configured_feature/test_entry.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "orespawn4:default", - "config": { - "name": "blargh", - "biomes": { "type": "orespawn4:allowall", "data": [] }, - "dimensions": { "type": "orespawn4:denylist", "data": [ "minecraft:the_end", "minecraft:the_nether" ] }, - "parameters": { - "maxHeight": 255, - "minHeight": 0, - "size": 32, - "variation": 16, - "frequency": 0.50 - }, - "replaces": { - "predicate_type": "minecraft:tag_match", - "tag": "minecraft:base_stone_overworld" - }, - "blocks": [ { "data": { "Name": "minecraft:coal_ore" }, "weight": 100 }, { "data": { "Name": "minecraft:iron_ore" }, "weight": 25 } ] - } -} diff --git a/src/main/resources/orespawn4.mixins.json b/src/main/resources/orespawn4.mixins.json deleted file mode 100644 index 9894d8d..0000000 --- a/src/main/resources/orespawn4.mixins.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "required": true, - "package": "com.mcmoddev.orespawn.utils.mixins", - "compatibilityLevel": "JAVA_8", - "refmap": "orespawn4.refmap.json", - "mixins": [ - "ServerAccessor", - "BiomeGenerationSettingsAccessor" - ], - "injectors": { - "defaultRequire": 1 - }, - "minVersion": "0.8" -} diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta deleted file mode 100644 index fe81797..0000000 --- a/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,7 +0,0 @@ -{ - "pack": { - "description": "OreSpawn4 Resources and Data", - "pack_format": 4, - "_comment": "A pack_format of 4 requires json lang files. Note: we require v4 pack meta for all mods." - } -} diff --git a/update.json b/update.json deleted file mode 100644 index 805cf5b..0000000 --- a/update.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "homepage": "http://minecraft.curseforge.com/projects/examplemod", - "promos": { - "1.12.2-latest": "1.0.0", - "1.12.1-latest": "1.0.0", - "1.12-latest": "1.0.0", - "1.11.2-latest": "0.0.0", - "1.11-latest": "0.0.0", - "1.10.2-latest": "0.0.0", - "1.10-latest": "0.0.0", - "1.9.4-latest": "0.0.0", - "1.9-latest": "0.0.0", - "1.8.9-latest": "0.0.0", - "1.8.8-latest": "0.0.0", - "1.8-latest": "0.0.0", - "1.7.10-latest": "0.0.0", - "1.7.2-latest": "0.0.0", - "1.6.4-latest": "0.0.0", - "1.12.2-recommended": "1.0.0", - "1.12.1-recommended": "1.0.0", - "1.12-recommended": "1.0.0", - "1.11.2-recommended": "0.0.0", - "1.11-recommended": "0.0.0", - "1.10.2-recommended": "0.0.0", - "1.10-recommended": "0.0.0", - "1.9.4-recommended": "0.0.0", - "1.9-recommended": "0.0.0", - "1.8.9-recommended": "0.0.0", - "1.8.8-recommended": "0.0.0", - "1.8-recommended": "0.0.0", - "1.7.10-recommended": "0.0.0", - "1.7.2-recommended": "0.0.0", - "1.6.4-recommended": "0.0.0" - }, - "1.12.2": { - "1.0.0": "Working Basemod, Block and Item Examples" - }, - "1.12.1": { - "1.0.0": "Working Basemod, Block and Item Examples" - }, - "1.12": { - "1.0.0": "Working Basemod, Block and Item Examples" - }, - "1.11.2": { - "0.0.0": "No Release" - }, - "1.11": { - "0.0.0": "No Release" - }, - "1.10.2": { - "0.0.0": "No Release" - }, - "1.10": { - "0.0.0": "No Release" - }, - "1.9.4": { - "0.0.0": "No Release" - }, - "1.9": { - "0.0.0": "No Release" - }, - "1.8.9": { - "0.0.0": "No Release" - }, - "1.8.8": { - "0.0.0": "No Release" - }, - "1.8": { - "0.0.0": "No Release" - }, - "1.7.10": { - "0.0.0": "No Release" - }, - "1.7.2": { - "0.0.0": "No Release" - }, - "1.6.4": { - "0.0.0": "No Release" - } -} From e92b7372c14b746edda34743a01e67307899a265 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Tue, 29 Jul 2025 12:03:51 -0400 Subject: [PATCH 37/37] code! --- settings.gradle | 11 + .../orespawn/crafttweaker/CrTConstants.java | 5 + .../orespawn/crafttweaker/OreSpawnPlugin.java | 56 ++++ .../mcmoddev/orespawn/crafttweaker/TODO.md | 23 ++ .../mcmoddev/orespawn/features/Clusters.java | 71 +++++ .../orespawn/features/NormalCloud.java | 90 ++++++ .../orespawn/features/VeinFeature.java | 273 ++++++++++++++++++ .../configs/ClusterConfiguration.java | 36 +++ .../configs/NormalCloudConfiguration.java | 33 +++ .../features/configs/VeinConfiguration.java | 60 ++++ .../java/com/mcmoddev/orespawn/misc/M.java | 23 ++ .../resources/META-INF/neoforge.mods.toml | 100 +++++++ 12 files changed, 781 insertions(+) create mode 100644 settings.gradle create mode 100644 src/main/java/com/mcmoddev/orespawn/crafttweaker/CrTConstants.java create mode 100644 src/main/java/com/mcmoddev/orespawn/crafttweaker/OreSpawnPlugin.java create mode 100644 src/main/java/com/mcmoddev/orespawn/crafttweaker/TODO.md create mode 100644 src/main/java/com/mcmoddev/orespawn/features/Clusters.java create mode 100644 src/main/java/com/mcmoddev/orespawn/features/NormalCloud.java create mode 100644 src/main/java/com/mcmoddev/orespawn/features/VeinFeature.java create mode 100644 src/main/java/com/mcmoddev/orespawn/features/configs/ClusterConfiguration.java create mode 100644 src/main/java/com/mcmoddev/orespawn/features/configs/NormalCloudConfiguration.java create mode 100644 src/main/java/com/mcmoddev/orespawn/features/configs/VeinConfiguration.java create mode 100644 src/main/java/com/mcmoddev/orespawn/misc/M.java create mode 100644 src/main/resources/META-INF/neoforge.mods.toml diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ada876e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,11 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = 'https://maven.neoforged.net/releases' } + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} diff --git a/src/main/java/com/mcmoddev/orespawn/crafttweaker/CrTConstants.java b/src/main/java/com/mcmoddev/orespawn/crafttweaker/CrTConstants.java new file mode 100644 index 0000000..61412e7 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/crafttweaker/CrTConstants.java @@ -0,0 +1,5 @@ +package com.mcmoddev.orespawn.crafttweaker; + +public class CrTConstants { + public static final String LOADER_NAME = "orespawn4"; +} diff --git a/src/main/java/com/mcmoddev/orespawn/crafttweaker/OreSpawnPlugin.java b/src/main/java/com/mcmoddev/orespawn/crafttweaker/OreSpawnPlugin.java new file mode 100644 index 0000000..1d00219 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/crafttweaker/OreSpawnPlugin.java @@ -0,0 +1,56 @@ +package com.mcmoddev.orespawn.crafttweaker; +import com.blamejared.crafttweaker.api.plugin.*; +import com.blamejared.crafttweaker.api.zencode.scriptrun.IScriptRunModuleConfigurator; +import com.mcmoddev.orespawn.OreSpawn; + +/* +TODO: Find docs on how to actually register a loader and work out the various @ZenClass/@ZenMethod requirements for + making things work. + */ +@CraftTweakerPlugin(OreSpawn.MODID+":crt_plugin") +public class OreSpawnPlugin implements ICraftTweakerPlugin { + @Override + public void registerLoaders(final ILoaderRegistrationHandler handler) { + handler.registerLoader(CrTConstants.LOADER_NAME); + } + + @Override + public void registerModuleConfigurators(final IScriptRunModuleConfiguratorRegistrationHandler handler) { + final IScriptRunModuleConfigurator defaultConfig = IScriptRunModuleConfigurator.createDefault(OreSpawn.MODID); + handler.registerConfigurator(CrTConstants.LOADER_NAME, defaultConfig); + } + + @Override + public void manageJavaNativeIntegration(final IJavaNativeIntegrationRegistrationHandler handler) { +/* + this.zenGatherer.listProviders(); + this.zenGatherer.onCandidates(candidate -> this.zenClassRegistrationManager.attemptRegistration(candidate.loader(), candidate.clazz(), handler)); + this.zenClassRegistrationManager.attemptDeferredRegistration(handler); */ + } + + @Override + public void registerLoadSource(final IScriptLoadSourceRegistrationHandler handler) { +/* + handler.registerLoadSource(CraftTweakerConstants.RELOAD_LISTENER_SOURCE_ID); + handler.registerLoadSource(CraftTweakerConstants.CLIENT_RECIPES_UPDATED_SOURCE_ID);*/ + } + + // last step, load scripts: + /* + final ScriptRunConfiguration configuration = new ScriptRunConfiguration( + CraftTweakerConstants.TAGS_LOADER_NAME, + CraftTweakerConstants.RELOAD_LISTENER_SOURCE_ID, // TODO("Custom load source?") + ScriptRunConfiguration.RunKind.EXECUTE + ); + + try { + CraftTweakerAPI.getScriptRunManager() + .createScriptRun(configuration) + .execute(); + } catch(final Throwable e) { + CraftTweakerCommon.logger().error("Unable to run tag scripts due to an error", e); + } + */ + @Override + public void initialize() {} +} diff --git a/src/main/java/com/mcmoddev/orespawn/crafttweaker/TODO.md b/src/main/java/com/mcmoddev/orespawn/crafttweaker/TODO.md new file mode 100644 index 0000000..156f0bd --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/crafttweaker/TODO.md @@ -0,0 +1,23 @@ +# TODO +## CraftTweaker Integration Specific +### Goals +Provide for simplified interface so pack creators can add new forms of ore-body generation +without needing to have it added in a hard-coded manner to MMD OreSpawn. + +### Items +1) Do Initialization Tasks, if any +2) Register Loader +3) Register block and blockstate brackets as needed +4) Add generics and interfaces to vanilla world-gen classes and types related + +### Notes +The last item on the above list is the most complex, overall. What to do about it is a +major question, as I'm unsure of a way to actually manage the creation of a +`public static final` for the CODEC, though I am going to attempt to make this happen, overall. + +That and the generic that wraps the Configuration containing the codec and provides the actual +ore-placement code are the most complex items. It might be best to just provide a basic setup +that wraps the vanilla class -- or perhaps one that wraps a custom class that provides for a +couple extra generically named values -- and then let the generation code provided by the +user be a lambda or similar ? + diff --git a/src/main/java/com/mcmoddev/orespawn/features/Clusters.java b/src/main/java/com/mcmoddev/orespawn/features/Clusters.java new file mode 100644 index 0000000..44ad811 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/Clusters.java @@ -0,0 +1,71 @@ +package com.mcmoddev.orespawn.features; + +import com.mcmoddev.orespawn.features.configs.ClusterConfiguration; +import com.mcmoddev.orespawn.misc.M; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; + +/** + * Generates clusters of ores or custom blocks within a circular area. + *

+ * For each cluster, picks a random position within a radius around the origin, + * checks the existing block at that position against the configured targets, + * and replaces it if it matches. + */ +public class Clusters extends Feature { + public Clusters() { + super(ClusterConfiguration.CODEC); + } + + /** + * Places clusters according to the provided configuration. + * + * @param context Contextual information for feature placement, including world, origin, and configuration. + * @return true if placement completed (Minecraft ignores this return value for features). + */ + @Override + public boolean place(FeaturePlaceContext context) { + ClusterConfiguration config = context.config(); + WorldGenLevel world = (WorldGenLevel) context.level(); + RandomSource random = context.random(); + BlockPos origin = context.origin(); + + // Compute radius and number of attempts based on config + int radius = config.spread / 2; + int maxClusters = (int) (Math.PI * radius * radius); + int clustersToPlace = Math.min(config.size, maxClusters); + + // World height bounds + int minBuildY = world.getMinBuildHeight(); + int maxBuildY = world.getMaxBuildHeight(); // exclusive + + // Generate each cluster + for (int i = 0; i < clustersToPlace; i++) { + // Pick a random offset within the circular spread + int offsetX = M.getPoint(0, config.spread, radius, random); + int offsetZ = M.getPoint(0, config.spread, radius, random); + int offsetY = random.nextInt(maxBuildY - minBuildY) + minBuildY; + + BlockPos targetPos = new BlockPos( + origin.getX() + offsetX, + offsetY, + origin.getZ() + offsetZ + ); + + // Check existing block and replace if it matches any target state + BlockState existingState = world.getBlockState(targetPos); + for (var targetState : config.targetStates) { + if (targetState.target.test(existingState, random)) { + // Use flag 2: no physics and no neighbor updates for performance + world.setBlock(targetPos, targetState.state, 2); + } + } + } + + return true; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/features/NormalCloud.java b/src/main/java/com/mcmoddev/orespawn/features/NormalCloud.java new file mode 100644 index 0000000..cb10669 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/NormalCloud.java @@ -0,0 +1,90 @@ +package com.mcmoddev.orespawn.features; + +import com.mcmoddev.orespawn.features.configs.NormalCloudConfiguration; +import com.mcmoddev.orespawn.misc.M; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; + +import java.util.HashSet; +import java.util.Set; + +/** + * Generates "cloud" formations of ores or custom blocks scattered within a circular area. + *

+ * Attempts up to size placements, skipping duplicates and positions outside range, + * replacing matching blocks without physics or neighbor updates for performance. + */ +public class NormalCloud extends Feature { + public NormalCloud() { + super(NormalCloudConfiguration.CODEC); + } + + /** + * Places scattered blocks (cloud) based on configuration parameters. + *

+ * For each placement attempt, picks a random offset within the configured spread, + * clamps Y to world bounds, skips duplicates, and replaces the block if it matches any target. + * Stops early if maxAttempts reached (twice the desired count) to avoid infinite loops. + * + * @param context Placement context including world, random, origin, and config + * @return true always (feature return value is ignored) + */ + @Override + public boolean place(FeaturePlaceContext context) { + NormalCloudConfiguration config = context.config(); + WorldGenLevel world = (WorldGenLevel) context.level(); + RandomSource random = context.random(); + BlockPos origin = context.origin(); + + // Compute half of configured spread as radius + int radius = config.spread / 2; + // Maximum distinct positions within circle + int desiredCount = (int) (Math.PI * radius * radius); + // Actual number of placements to attempt + int placementsNeeded = Math.min(config.size, desiredCount); + + // World height constraints + int minY = world.getMinBuildHeight(); + int maxY = world.getMaxBuildHeight(); // exclusive + + // Track positions already processed to avoid duplicates + Set seenPositions = new HashSet<>(); + int attempts = 0; + int maxAttempts = placementsNeeded * 2; + + while (seenPositions.size() < placementsNeeded && attempts < maxAttempts) { + attempts++; + + // Random offset within circular spread + int offsetX = M.getPoint(0, config.spread, radius, random); + int offsetZ = M.getPoint(0, config.spread, radius, random); + int offsetY = random.nextInt(maxY - minY) + minY; + + BlockPos targetPos = new BlockPos( + origin.getX() + offsetX, + offsetY, + origin.getZ() + offsetZ + ); + + // Skip duplicates + if (!seenPositions.add(targetPos)) { + continue; + } + + // Check existing block and replace if it matches any target state + BlockState existingState = world.getBlockState(targetPos); + for (var targetState : config.targetStates) { + if (targetState.target.test(existingState, random)) { + // Flag=2: no physics, no neighbor updates + world.setBlock(targetPos, targetState.state, 2); + } + } + } + + return true; + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/features/VeinFeature.java b/src/main/java/com/mcmoddev/orespawn/features/VeinFeature.java new file mode 100644 index 0000000..1271ef8 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/VeinFeature.java @@ -0,0 +1,273 @@ +package com.mcmoddev.orespawn.features; + +import com.mcmoddev.orespawn.OreSpawn; +import com.mcmoddev.orespawn.features.configs.VeinConfiguration; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.BulkSectionAccess; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class VeinFeature extends Feature { + private static final String eMess = "Value %d out of range (1-6)"; + + public VeinFeature() { + super(VeinConfiguration.CODEC); + OreSpawn.LOGGER.info("VeinFeature created"); + } + + @Override + public boolean place(FeaturePlaceContext pContext) { + RandomSource randomsource = pContext.random(); + BlockPos blockpos = pContext.origin(); + WorldGenLevel worldgenlevel = pContext.level(); + VeinConfiguration veinconfiguration = pContext.config(); + + double startX = blockpos.getX(); + double startY = blockpos.getY(); + double startZ = blockpos.getZ(); + int length = randomsource.nextInt(veinconfiguration.minLength, veinconfiguration.maxLength); + return doPlacement(worldgenlevel, randomsource, veinconfiguration, startX, startY, startZ, length); + } + + private boolean doPlacement(WorldGenLevel pLevel, RandomSource pRandom, VeinConfiguration pConfig, double startX, double startY, double startZ, int length) { + OreSpawn.LOGGER.info("VeinFeature called to spawn a vein at {}, {}, {}", startX, startY, startZ); + int cn = 0; + int ls = -1; + int ns = -1; + BlockPos curpos = new BlockPos((int) startX, (int) startY, (int) startZ); + List> seen = new ArrayList<>(); + while (cn <= length) { + ns = pRandom.nextInt(1, 6); + if (ns == ls) + while (ns == ls) + ns = pRandom.nextInt(1, 6); + + if (ls == -1) + seen.add(Pair.of(curpos, ns)); + curpos = switch (ns) { + case 1 -> curpos.above(); + case 2 -> curpos.below(); + case 3 -> curpos.east(); + case 4 -> curpos.west(); + case 5 -> curpos.north(); + case 6 -> curpos.south(); + default -> throw new RuntimeException(String.format(eMess, ns)); + }; + + ls = ns; + if (seen.contains(Pair.of(curpos, ls))) break; // early exit, we've crossed our previous track + cn += 1; + } + seen.add(Pair.of(curpos, ns)); + seen.forEach(bp -> makeNodeAt(pLevel, pConfig, bp, pRandom)); + return true; + } + + private void makeNodeAt(WorldGenLevel pLevel, VeinConfiguration pConfig, Pair pLoc, RandomSource pRandom) { + // the right side of each loc determines if the node generates vertical or horizontal + switch (pLoc.getRight()) { + case 1: + case 2: + makeHorizontal(pLevel, pLoc.getLeft(), pConfig, pRandom); + break; + case 3: + case 4: + makeVertical(pLevel, pLoc.getLeft(), pConfig, pRandom, true); + break; + case 5: + case 6: + makeVertical(pLevel, pLoc.getLeft(), pConfig, pRandom, false); + break; + default: + throw new RuntimeException(String.format(eMess, pLoc.getRight())); + } + } + + private static double getRadiusOfArea(int area) { + double base = (double)area/Math.PI; + double res = Math.sqrt(base); + return res; + } + + private static Pair paraCirc(double r, double curT) { + return Pair.of(r * Math.sin(curT), r * Math.cos(curT)); + } + + private static Pair paraCircCoords(int cx, int cy, double r, double curT) { + Pair base = paraCirc(r, curT); + return Pair.of((int) (cx + base.getLeft()), (int) (cy + base.getRight())); + } + + private void makeHorizontal(WorldGenLevel pLevel, BlockPos pos, VeinConfiguration pConfig, RandomSource pRandom) { + OreSpawn.LOGGER.debug("[Vein] makeHorizontal start at {} size={} maxR={}", pos, pConfig.size, getRadiusOfArea(pConfig.size)); + try (BulkSectionAccess bulksectionaccess = new BulkSectionAccess(pLevel)) { + List> placed = new LinkedList<>(); + + BlockPos.MutableBlockPos accessPos = pos.mutable(); + //OreSpawn.LOGGER.info("VeinFeature starting at {}", accessPos); + + if (pLevel.ensureCanWrite(accessPos)) { + LevelChunkSection section = bulksectionaccess.getSection(accessPos); + if (section != null) { + //OreSpawn.LOGGER.info("section != null"); + int relX = SectionPos.sectionRelative(accessPos.getX()); + int relY = SectionPos.sectionRelative(accessPos.getY()); + int relZ = SectionPos.sectionRelative(accessPos.getZ()); + BlockState blockstate = section.getBlockState(relX, relY, relZ); + if (!placed.contains(Pair.of(accessPos.getX(), accessPos.getZ()))) { + //OreSpawn.LOGGER.info("!placed.contains(Pair.of(...)) targets: {}", pConfig.targetStates.size()); + for (VeinConfiguration.TargetBlockState tgt : pConfig.targetStates) { + //OreSpawn.LOGGER.info("Target state: {} ({})-- test: {}", tgt, blockstate, tgt.target.test(blockstate, pRandom)); + if (tgt.target.test(blockstate, pRandom)) { + placed.add(Pair.of(accessPos.getX(), accessPos.getZ())); + //OreSpawn.LOGGER.info("calling setBlock (pLevel.setBlock(...))"); + //pLevel.setBlock(accessPos, tgt.state, 2); + + int rx = SectionPos.sectionRelative(accessPos.getX()); + int ry = SectionPos.sectionRelative(accessPos.getY()); + int rz = SectionPos.sectionRelative(accessPos.getZ()); + OreSpawn.LOGGER.info("section.setBlockState({}, {}, {}, {}, false)", rx, ry, rz, tgt.state); + section.setBlockState(rx, ry, rz, tgt.state, false); + } + } + } + } + } + //double maxRadius = Math.min(getRadiusOfArea(pConfig.size), 8); // TODO: make this configurable - added for now for testing + //for (double r = 1; r <= maxRadius; r++) { // maxRadius=getRadiusOfArea(pConfig.size) + + + for (double r = 1; r <= getRadiusOfArea(pConfig.size); r++) { + double div = Math.pow(r*2+1, 2); + double step = Math.TAU / div; + for (double c = 0; c <= Math.TAU; c += step) { + int left = pos.getX(); + int right = pos.getZ(); + Pair tl = paraCircCoords(left, right, r, c); + accessPos.set(tl.getLeft(), pos.getY(), tl.getRight()); + if (pLevel.ensureCanWrite(accessPos)) { + LevelChunkSection section = bulksectionaccess.getSection(accessPos); + if (section != null) { + //OreSpawn.LOGGER.info("section != null"); + int relX = SectionPos.sectionRelative(accessPos.getX()); + int relY = SectionPos.sectionRelative(accessPos.getY()); + int relZ = SectionPos.sectionRelative(accessPos.getZ()); + BlockState blockstate = section.getBlockState(relX, relY, relZ); + if (!placed.contains(tl)) { + //OreSpawn.LOGGER.info("!placed.contains(tl)"); + for (VeinConfiguration.TargetBlockState tgt : pConfig.targetStates) { + //OreSpawn.LOGGER.info("Target state: {} ({})-- test: {}", tgt, blockstate, tgt.target.test(blockstate, pRandom)); + if (tgt.target.test(blockstate, pRandom)) { + placed.add(tl); + //OreSpawn.LOGGER.info("calling setBlock (pLevel.setBlock(...))"); + //pLevel.setBlock(accessPos, tgt.state, 2); + + int rx = SectionPos.sectionRelative(accessPos.getX()); + int ry = SectionPos.sectionRelative(accessPos.getY()); + int rz = SectionPos.sectionRelative(accessPos.getZ()); + OreSpawn.LOGGER.info("section.setBlockState({}, {}, {}, {}, false)", rx, ry, rz, tgt.state); + section.setBlockState(rx, ry, rz, tgt.state, false); + } + } + } + } + } + } + } + } + OreSpawn.LOGGER.debug("[Vein] makeHorizontal done at {}", pos); + } + + private void makeVertical(WorldGenLevel pLevel, BlockPos pos, VeinConfiguration pConfig, RandomSource pRandom, boolean northSouth) { + OreSpawn.LOGGER.debug("[Vein] makeVertical start at {} size={} maxR={}", pos, pConfig.size, getRadiusOfArea(pConfig.size)); + // northsouth == true, manipulate XY else manipulate ZY + try (BulkSectionAccess bulksectionaccess = new BulkSectionAccess(pLevel)) { + List> placed = new LinkedList<>(); + BlockPos.MutableBlockPos accessPos = pos.mutable(); + OreSpawn.LOGGER.info("VeinFeature starting at {} **{}", accessPos, northSouth); + + if (pLevel.ensureCanWrite(accessPos)) { + LevelChunkSection section = bulksectionaccess.getSection(accessPos); + if (section != null) { + //OreSpawn.LOGGER.info("section != null"); + int relX = SectionPos.sectionRelative(accessPos.getX()); + int relY = SectionPos.sectionRelative(accessPos.getY()); + int relZ = SectionPos.sectionRelative(accessPos.getZ()); + BlockState blockstate = section.getBlockState(relX, relY, relZ); + if (!placed.contains(Pair.of(accessPos.getX(), northSouth?accessPos.getY():accessPos.getZ()))) { + //OreSpawn.LOGGER.info("!placed.contains(Pair.of(...))"); + for (VeinConfiguration.TargetBlockState tgt : pConfig.targetStates) { + //OreSpawn.LOGGER.info("Target state: {} ({})-- test: {}", tgt, blockstate, tgt.target.test(blockstate, pRandom)); + if (tgt.target.test(blockstate, pRandom)) { + placed.add(Pair.of(accessPos.getX(), northSouth?accessPos.getY():accessPos.getZ())); + //OreSpawn.LOGGER.info("calling setBlock (pLevel.setBlock(...))"); + //pLevel.setBlock(accessPos, tgt.state, 2); + + int rx = SectionPos.sectionRelative(accessPos.getX()); + int ry = SectionPos.sectionRelative(accessPos.getY()); + int rz = SectionPos.sectionRelative(accessPos.getZ()); + OreSpawn.LOGGER.info("section.setBlockState({}, {}, {}, {}, false)", rx, ry, rz, tgt.state); + section.setBlockState(rx, ry, rz, tgt.state, false); + } + } + } + } + } + + for (double r = 1; r <= getRadiusOfArea(pConfig.size); r++) { + double div = Math.pow(r*2+1, 2); + double step = Math.TAU / div; + for (double c = 0; c <= Math.TAU; c += step) { + int left = pos.getX(); + int right = northSouth?pos.getY():pos.getZ(); + Pair tl = paraCircCoords(left, right, r, c); + if (northSouth) { + accessPos.set(tl.getLeft(), tl.getRight(), pos.getZ()); + } else { + accessPos.set(pos.getX(), tl.getRight(), tl.getLeft()); + } + + if (pLevel.ensureCanWrite(accessPos)) { + LevelChunkSection section = bulksectionaccess.getSection(accessPos); + if (section != null) { + //OreSpawn.LOGGER.info("section != null"); + int relX = SectionPos.sectionRelative(accessPos.getX()); + int relY = SectionPos.sectionRelative(accessPos.getY()); + int relZ = SectionPos.sectionRelative(accessPos.getZ()); + BlockState blockstate = section.getBlockState(relX, relY, relZ); + if (!placed.contains(tl)) { + //OreSpawn.LOGGER.info("!placed.contains(tl)"); + for (VeinConfiguration.TargetBlockState tgt : pConfig.targetStates) { + //OreSpawn.LOGGER.info("Target state: {} ({})-- test: {}", tgt, blockstate, tgt.target.test(blockstate, pRandom)); + if (tgt.target.test(blockstate, pRandom)) { + placed.add(tl); + //OreSpawn.LOGGER.info("calling setBlock (pLevel.setBlock(...))"); + //pLevel.setBlock(accessPos, tgt.state, 2); + + int rx = SectionPos.sectionRelative(accessPos.getX()); + int ry = SectionPos.sectionRelative(accessPos.getY()); + int rz = SectionPos.sectionRelative(accessPos.getZ()); + OreSpawn.LOGGER.info("section.setBlockState({}, {}, {}, {}, false)", rx, ry, rz, tgt.state); + section.setBlockState(rx, ry, rz, tgt.state, false); + } + } + } + } + } + } + } + } + + OreSpawn.LOGGER.debug("[Vein] makeVertical done at {}", pos); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/features/configs/ClusterConfiguration.java b/src/main/java/com/mcmoddev/orespawn/features/configs/ClusterConfiguration.java new file mode 100644 index 0000000..128c52e --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/configs/ClusterConfiguration.java @@ -0,0 +1,36 @@ +package com.mcmoddev.orespawn.features.configs; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest; + +import java.util.List; + +public class ClusterConfiguration implements FeatureConfiguration { + public final List targetStates; + public final int size; + public final int spread; + public final int nodeSize; + + public static final Codec CODEC = RecordCodecBuilder.create( + (builder) -> builder.group( + Codec.list(VeinConfiguration.TargetBlockState.CODEC).fieldOf("targets").forGetter((config) -> config.targetStates ), + Codec.INT.fieldOf("size").forGetter((config) -> config.size), + Codec.INT.fieldOf("spread").forGetter((config) -> config.spread), + Codec.INT.fieldOf("node_size").forGetter((config) -> config.nodeSize)) + .apply(builder, ClusterConfiguration::new)); + + public ClusterConfiguration(List targetStates, int size, int spread, int nodeSize) { + this.targetStates = targetStates; + this.size = size; + this.spread = spread; + this.nodeSize = nodeSize; + } + + public ClusterConfiguration(RuleTest target, BlockState state, int size, int spread, int nodeSize) { + this(ImmutableList.of(new VeinConfiguration.TargetBlockState(target, state)), size, spread, nodeSize); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/features/configs/NormalCloudConfiguration.java b/src/main/java/com/mcmoddev/orespawn/features/configs/NormalCloudConfiguration.java new file mode 100644 index 0000000..1285ee7 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/configs/NormalCloudConfiguration.java @@ -0,0 +1,33 @@ +package com.mcmoddev.orespawn.features.configs; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest; + +import java.util.List; + +public class NormalCloudConfiguration implements FeatureConfiguration { + public final List targetStates; + public final int size; + public final int spread; + + public static final Codec CODEC = RecordCodecBuilder.create( + (builder) -> builder.group( + Codec.list(VeinConfiguration.TargetBlockState.CODEC).fieldOf("targets").forGetter((config) -> config.targetStates ), + Codec.INT.fieldOf("size").forGetter((config) -> config.size), + Codec.INT.fieldOf("spread").forGetter((config) -> config.spread) ).apply(builder, NormalCloudConfiguration::new)); + + + public NormalCloudConfiguration(List targetStates, int size, int spread) { + this.targetStates = targetStates; + this.size = size; + this.spread = spread; + } + + public NormalCloudConfiguration(RuleTest target, BlockState state, int size, int spread) { + this(ImmutableList.of(new VeinConfiguration.TargetBlockState(target, state)), size, spread); + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/features/configs/VeinConfiguration.java b/src/main/java/com/mcmoddev/orespawn/features/configs/VeinConfiguration.java new file mode 100644 index 0000000..3139690 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/features/configs/VeinConfiguration.java @@ -0,0 +1,60 @@ +package com.mcmoddev.orespawn.features.configs; + +import com.google.common.collect.ImmutableList; +import com.mcmoddev.orespawn.OreSpawn; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest; + +import java.util.List; + +public class VeinConfiguration implements FeatureConfiguration { + public final List targetStates; + public final int size; + public final int maxLength; + public final int minLength; + public final float frequency; + + public static final Codec CODEC = RecordCodecBuilder.create( + (builder) -> builder.group( + Codec.list(TargetBlockState.CODEC).fieldOf("targets").forGetter((config) -> config.targetStates ), + Codec.INT.fieldOf("size").forGetter((config) -> config.size), + Codec.INT.fieldOf("max_length").forGetter((config) -> config.maxLength), + Codec.INT.fieldOf("min_length").forGetter((config) -> config.minLength), + Codec.FLOAT.fieldOf("frequency").forGetter((config) -> config.frequency) + ).apply(builder, VeinConfiguration::new) + ); + + public VeinConfiguration(List targets, int size, int maxlength, int minlength, float frequency) { + this.targetStates = targets; + this.size = size; + this.maxLength = maxlength; + this.minLength = minlength; + this.frequency = frequency; + } + + public VeinConfiguration(RuleTest target, BlockState state, int size, int maxlength, int minlength, float frequency) { + this(ImmutableList.of(new TargetBlockState(target, state)), size, maxlength, minlength, frequency); + OreSpawn.LOGGER.info("Vein of size {} and a length between {} and {} configured", size, minlength, maxlength); + } + + + public static class TargetBlockState { + public static final Codec CODEC = RecordCodecBuilder.create( + p_161039_ -> p_161039_.group( + RuleTest.CODEC.fieldOf("target").forGetter(p_161043_ -> p_161043_.target), + BlockState.CODEC.fieldOf("state").forGetter(p_161041_ -> p_161041_.state) + ) + .apply(p_161039_, VeinConfiguration.TargetBlockState::new) + ); + public final RuleTest target; + public final BlockState state; + + TargetBlockState(RuleTest pRule, BlockState pState) { + this.target = pRule; + this.state = pState; + } + } +} diff --git a/src/main/java/com/mcmoddev/orespawn/misc/M.java b/src/main/java/com/mcmoddev/orespawn/misc/M.java new file mode 100644 index 0000000..d5aa686 --- /dev/null +++ b/src/main/java/com/mcmoddev/orespawn/misc/M.java @@ -0,0 +1,23 @@ +package com.mcmoddev.orespawn.misc; + +import net.minecraft.util.RandomSource; + +public class M { + + public static double triangularDistribution(double a, double b, double c, RandomSource random) { + double base = (c - a) / (b - a); + double rand = random.nextDouble(); + + if (rand < base) { + return a + Math.sqrt(rand * (b - a) * (c - a)); + } else { + return b - Math.sqrt((1 - rand) * (b - a) * (b - c)); + } + } + public static int getPoint(int lowerBound, int upperBound, int median, RandomSource random) { + int t = (int)Math.round(triangularDistribution((float)lowerBound, (float)upperBound, (float)median, random)); + return t - median; + } + + +} diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..f0156b6 --- /dev/null +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,100 @@ +# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory + +# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. +loaderVersion="${loader_version_range}" #mandatory + +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="${mod_license}" + +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory + +# The modid of the mod +modId="${mod_id}" #mandatory + +# The version number of the mod +version="${mod_version}" #mandatory + +# A display name for the mod +displayName="${mod_name}" #mandatory + +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional + +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional + +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional + +# A text field displayed in the mod UI +#credits="" #optional + +# A text field displayed in the mod UI +authors="${mod_authors}" #optional + +# Display Test controls the display for your mod in the server connection screen +# MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. +# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. +# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. +# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. +# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. +#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) + +# The description text for the mod (multi line!) (#mandatory) +description='''${mod_description}''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +#[[mixins]] +#config="${mod_id}.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.${mod_id}]] #optional + # the modid of the dependency + modId="neoforge" #mandatory + # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). + # 'required' requires the mod to exist, 'optional' does not + # 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning + type="required" #mandatory + # Optional field describing why the dependency is required or why it is incompatible + # reason="..." + # The version range of the dependency + versionRange="${neo_version_range}" #mandatory + # An ordering relationship for the dependency. + # BEFORE - This mod is loaded BEFORE the dependency + # AFTER - This mod is loaded AFTER the dependency + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT, or SERVER + side="BOTH" + +# Here's another dependency +[[dependencies.${mod_id}]] + modId="minecraft" + type="required" + # This version range declares a minimum of the current minecraft version up to but not including the next major version + versionRange="${minecraft_version_range}" + ordering="NONE" + side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)"