Compare commits

..

94 Commits

Author SHA1 Message Date
Zoë Gidiere db930739d7 Merge pull request #545 from TheNick24/dev/1.21.11
feat: update minestom to 1.21.11
2026-04-28 17:47:42 +00:00
Nico Weber 9686372238 feat: update minestom to 1.21.11 2025-12-21 01:41:15 +01:00
Zoe Gidiere 2120dde0db WIP on master 2025-12-19 06:13:44 -07:00
Zoë Gidiere f780450a7c Merge pull request #543 from everbuild-org/feat/minestom-ver-1.21.11
Minestom 1.21.11 Support
2025-12-19 03:09:07 +00:00
Zoe Gidiere e870a9eb8d Merge branch 'master' into dev/1.21.11 2025-12-18 20:07:58 -07:00
Zoe Gidiere c6eb2f49f3 Revert "Merge pull request #538 from ryzech/fix/caffeine-relocate"
This reverts commit 6929de7b61, reversing
changes made to a159debe3e.
2025-12-17 01:20:33 -07:00
Zoë Gidiere 6929de7b61 Merge pull request #538 from ryzech/fix/caffeine-relocate
Relocate caffeine to fix conflicts with other mods/plugins
2025-12-14 16:11:16 +00:00
Zoe Gidiere a159debe3e update publishing config 2025-12-13 17:55:17 -07:00
Zoe Gidiere 99e2907b2d add java doc build 2025-12-13 17:45:47 -07:00
Zoe Gidiere ac71b3ec0c Fix java doc dir 2025-12-13 17:41:21 -07:00
Zoe Gidiere e184937743 WIP Jenkinsfile 2025-12-13 17:34:13 -07:00
Zoe Gidiere 8c155c78eb step arg as min res 2025-12-13 11:35:39 -07:00
Zoe Gidiere fc779e1120 Auto-mode 2025-12-13 11:32:29 -07:00
Zoe Gidiere be964da4fa paper async 2025-12-13 10:48:18 -07:00
Zoe Gidiere 6ca401413b Terra Search Command 2025-12-13 10:34:01 -07:00
Zoe Gidiere e556e2bca1 Update Seismic 2025-12-13 10:33:18 -07:00
Zoe Gidiere 1fc97a480a Small biomechunk allocation opt 2025-12-11 14:44:32 -07:00
Zoe Gidiere bba55f2669 Align TriStateIntCache 2025-12-11 01:56:20 -07:00
Christian Bergschneider e99ae48f1a build: address review comments 2025-12-11 08:55:49 +00:00
Zoe Gidiere 97b4ea6d94 Improve PaddedGridDistributor Performance 2025-12-10 23:17:06 -07:00
Zoe Gidiere cf4f7822e2 Download JavaDocs and Sources in Idea 2025-12-10 23:16:39 -07:00
Zoe Gidiere d6285a5901 Improve TriStateintCache with Unsafe 2025-12-10 22:33:49 -07:00
Christian Bergschneider 2e2b315ab7 ci: bump java version to 25-tem 2025-12-11 03:40:32 +01:00
Christian Bergschneider 04554b886a chore: bump minestom to 1.21.10 2025-12-11 03:39:24 +01:00
Christian Bergschneider d3429626b5 chore: update gradle to version 9.2.1 to allow jvm25 2025-12-11 03:39:18 +01:00
Zoe Gidiere 8b933b0d5c Merge remote-tracking branch 'origin/master' 2025-12-10 19:28:43 -07:00
Zoe Gidiere f9a5dfbfce Update seismic 2025-12-10 19:28:39 -07:00
Zoë Gidiere 24bca3ed98 Merge pull request #542 from everbuild-org/fix/minestom-nbt-additions
Minestom Nbt Support
2025-12-10 18:57:19 -07:00
Zoe Gidiere f8f6b0b4bc Reformat 2025-12-10 18:52:00 -07:00
Zoe Gidiere dd2f0365b0 More Extrusion Opts 2025-12-10 18:49:03 -07:00
Christian Bergschneider ae2d801be0 perf: reduce object churn by swapping to long keys in GeneratedChunkCache 2025-12-11 02:33:40 +01:00
Christian Bergschneider 8578bba7b9 perf: reduce object churn by using minestom block states 2025-12-11 02:00:13 +01:00
Christian Bergschneider d262831107 feat: add basic minestom NBT additions and improve block state parser performance 2025-12-11 01:30:40 +01:00
Zoe Gidiere ddc8cc7db5 Reformat 2025-12-10 02:55:26 -07:00
Zoe Gidiere 12f1b3f8fc Use ASM to to compile extrusion pipelines 2025-12-10 02:54:44 -07:00
Zoe Gidiere dc7c57d1a3 Update seismic 2 2025-12-10 02:18:10 -07:00
OakLoaf 349ce6654c Adjusted list of supported versions 2025-12-09 21:35:09 +00:00
OakLoaf b04156bb45 Started migration to 1.21.11 2025-12-09 21:32:28 +00:00
Zoe Gidiere 309fb5af96 Update Seismic 2025-12-08 13:29:10 -07:00
RyzechDev 9d747aed71 Relocate caffeine to fix conflicts with other mods/plugins 2025-11-19 10:14:22 -06:00
Zoë Gidiere 3cf11a9ad4 Merge pull request #534 from AllayMC/feat/meta-pack
feat: add support for meta pack in allay platform
2025-10-14 13:34:24 -06:00
Zoë Gidiere 9f766b0647 Merge pull request #535 from PolyhedralDev/dev/1.21.10
Cleaned up Bukkit NMS bindings and marked 1.21.10 as supported
2025-10-14 13:33:51 -06:00
daoge_cmd 8fa3978dc8 feat: update resource files 2025-10-14 22:10:35 +08:00
OakLoaf c282e55f90 Adjusted paper dependency 2025-10-13 12:16:43 +01:00
OakLoaf 8b62badbdb Moved nms packages directly into the nms gradle module 2025-10-13 10:49:06 +01:00
OakLoaf 8bceb255c0 Corrected version formatting in supported versions list 2025-10-13 10:40:40 +01:00
OakLoaf ad80769d67 Removed version specific bindings 2025-10-12 22:48:41 +01:00
daoge_cmd 9d0fa0a7c4 Merge branch 'master' into feat/meta-pack 2025-10-10 10:04:15 +08:00
daoge_cmd 8c532ede8e fix: ignore the block when out of bounds 2025-10-10 10:03:01 +08:00
Zoë Gidiere c3d26527a7 parallel pack loading 2025-10-09 17:55:29 -06:00
Zoë Gidiere 1fe56335c8 Remove bufferedimage 2025-10-09 17:38:08 -06:00
Zoë Gidiere 3b377d91ee fix concurrency issues in openregistryimpl 2025-10-09 17:34:06 -06:00
daoge_cmd 0144200ec9 doc: update comment 2025-10-10 00:46:55 +08:00
daoge_cmd d1ad3d04e1 feat: add support for meta pack 2025-10-10 00:30:38 +08:00
Zoë Gidiere d640b49ded Merge pull request #533 from everbuild-org/fix/minestom-platform-fixes
Minestom 1.21.8/9/10 & Metapack support
2025-10-07 23:49:46 -06:00
Zoë Gidiere ecbfd1740c Fabric 1.21.10 2025-10-07 17:55:37 -06:00
Zoe Gidiere dc5952add7 WIP 1.21.10 2025-10-07 17:15:07 -06:00
Christian Bergschneider 1b6ebeb05f fix: improve particle handling and block state retrieval 2025-10-07 23:32:30 +02:00
Christian Bergschneider 234ff3e49c feat: actually do biome preloading 2025-10-07 23:23:54 +02:00
Christian Bergschneider 243c523b57 feat: pre-add biomes to registry to prevent future modifications 2025-10-07 23:06:58 +02:00
Christian Bergschneider 1700650753 feat: add methods to pack by meta and default meta in TerraMinestomWorldBuilder 2025-10-07 22:54:15 +02:00
Christian Bergschneider 12d2221d49 fix: ignore and warn on snbt on particles 2025-10-07 21:38:49 +02:00
Christian Bergschneider 42e1adfc3a chore: update minestom dependency 2025-10-07 21:38:26 +02:00
Zoë Gidiere 4ff91c9fea Revert "Update gradle"
This reverts commit 81bc51f5a1.
2025-10-07 12:53:36 -06:00
Zoe Gidiere 81bc51f5a1 Update gradle 2025-10-07 11:54:36 -06:00
Zoë Gidiere ac98726f81 Cleanup Fabric BlockState and EntityType Extended implementation 2025-10-06 18:12:42 -06:00
Zoë Gidiere 1d2c6d4294 Synchronize pack loading 2025-10-05 23:58:53 -06:00
Zoë Gidiere 9ca7014344 Entity SNBT Support and Cleanup 2025-10-05 23:50:29 -06:00
Zoë Gidiere 8d153998fa Reformat Code 2025-10-05 21:55:16 -06:00
Zoë Gidiere 2b09ed8fd9 Simplify mixin implementations
Refactors mixin implementations to remove unnecessary casting.

This change improves code readability and reduces redundancy by directly accessing methods and fields within the mixin context, rather than relying on casting to the target class.
2025-10-05 21:51:40 -06:00
Zoë Gidiere 96493ede15 Merge remote-tracking branch 'origin/master' 2025-10-05 20:24:25 -06:00
Zoë Gidiere 48586eb523 Fix SNBT writing 2025-10-05 20:23:54 -06:00
Zoë Gidiere a80b94ad45 Unwrap BlockState in Chunk Gen 2025-10-05 17:03:51 -06:00
Zoë Gidiere 089850d633 Support for SNBT in structures 2025-10-05 17:03:45 -06:00
Zoë Gidiere 42f3c56b71 Extended BlockState API 2025-10-05 16:47:12 -06:00
Zoë Gidiere 874ef56025 Update seismic 2025-10-05 16:44:49 -06:00
Zoë Gidiere 06f04005ea Merge pull request #526 from AllayMC/feat/allay-update
feat: update allay-api and resource files
2025-10-04 13:55:02 -06:00
daoge_cmd 774d076f77 build: enable build for allay platform 2025-10-04 17:21:22 +08:00
daoge_cmd e4561bd48f Merge branch 'master' into feat/allay-update
# Conflicts:
#	platforms/allay/src/main/java/com/dfsek/terra/allay/Mapping.java
#	platforms/allay/src/main/java/com/dfsek/terra/allay/handle/AllayItemHandle.java
2025-10-04 17:19:01 +08:00
daoge_cmd 550a037661 chore: remove unused okaeri maven repo 2025-10-04 17:14:49 +08:00
daoge_cmd 3b25e82a73 feat: use allay-api 0.12.0 2025-10-04 17:14:06 +08:00
Zoë Gidiere 9b3a105672 Fix game metapack loading 2025-10-03 19:07:00 -06:00
Zoë Gidiere 10558b5446 fix typo 2025-10-03 18:24:00 -06:00
Zoë Gidiere 326300bcce update resource logic for metapacks 2025-10-03 18:10:45 -06:00
Zoë Gidiere a7826dec49 add metapacks to default commented ingore list 2025-10-03 17:29:30 -06:00
Zoë Gidiere fd3d1ce830 Change ConstantRage check to only check if greater than 2025-10-03 17:11:12 -06:00
Zoë Gidiere 84a6cd0c26 fix bukkit spawning consistency 2025-10-03 15:49:55 -06:00
Zoë Gidiere d1faac8b96 Pull in remaining packs 2025-10-03 15:27:48 -06:00
Zoë Gidiere de4656d01f new pack name 2025-10-03 14:39:27 -06:00
Zoë Gidiere 6dba2e9394 Use latest OW 2025-10-03 13:59:54 -06:00
Zoë Gidiere 8a8db4a9b8 Merge pull request #445 from PolyhedralDev/dev/7.0-2
Dev/7.0
2025-10-03 01:01:49 -06:00
daoge_cmd 3ef60f4b33 feat: adapt allay api 0.12.0-SHAPSHOT (waiting for 0.12.0 release) 2025-09-30 23:12:12 +08:00
daoge_cmd b7864bb6fb feat: use https://github.com/misode/mcmeta to replace the old je_block_default_states.json file (it's renamed to je_blocks.json now). 2025-08-21 11:51:17 +08:00
daoge_cmd 757ed6ad4d feat: update allay-api and mapping files 2025-08-21 00:11:36 +08:00
133 changed files with 2394 additions and 4391 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v4.7.1
with:
java-version: '21'
java-version: '25'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
Vendored
+149
View File
@@ -0,0 +1,149 @@
pipeline {
agent any
tools {
jdk "Temurin Java 21"
}
triggers {
githubPush()
}
environment {
DISCORD_WEBHOOK_URL = credentials('polydev-discord-webhook-url')
}
stages {
stage('Checkout') {
steps {
scmSkip(deleteBuild: true)
}
}
stage('Setup Gradle') {
steps {
sh 'chmod +x gradlew'
}
}
stage('Build') {
steps {
withGradle {
sh './gradlew build --rerun-tasks -x check'
sh './gradlew javadoc'
}
}
post {
success {
archiveArtifacts artifacts: 'platforms/fabric/build/libs/Terra-fabric*.jar,platforms/bukkit/build/libs/Terra-bukkit*-shaded.jar,platforms/allay/build/libs/Terra-allay*.jar,platforms/minestom/build/libs/Terra-minestom*.jar', fingerprint: true, onlyIfSuccessful: true
javadoc javadocDir: 'common/api/build/docs/javadoc', keepAll: true
}
}
}
stage('Tests') {
steps {
withGradle {
sh './gradlew test --rerun-tasks'
}
}
}
// stage('Deploy to snapshots repositories') {
// when {
// allOf {
// not { buildingTag() }
// not { expression { env.TAG_NAME != null && env.TAG_NAME.matches('v\\d+\\.\\d+\\.\\d+') } }
// }
// }
//
// steps {
// withCredentials([
// string(credentialsId: 'maven-signing-key', variable: 'ORG_GRADLE_PROJECT_signingKey'),
// string(credentialsId: 'maven-signing-key-password', variable: 'ORG_GRADLE_PROJECT_signingPassword'),
// usernamePassword(
// credentialsId: 'solo-studios-maven',
// passwordVariable: 'ORG_GRADLE_PROJECT_SoloStudiosSnapshotsPassword',
// usernameVariable: 'ORG_GRADLE_PROJECT_SoloStudiosSnapshotsUsername'
// )
// ]) {
// withGradle {
// sh './gradlew publishAllPublicationsToSoloStudiosSnapshotsRepository'
// }
// }
// }
// }
stage('Deploy to releases repositories') {
// when {
// allOf {
// buildingTag()
// expression { env.TAG_NAME != null && env.TAG_NAME.matches('v\\d+\\.\\d+\\.\\d+') }
// }
// }
steps {
withCredentials([
string(credentialsId: 'maven-signing-key', variable: 'ORG_GRADLE_PROJECT_signingKey'),
string(credentialsId: 'maven-signing-key-password', variable: 'ORG_GRADLE_PROJECT_signingPassword'),
usernamePassword(
credentialsId: 'solo-studios-maven',
passwordVariable: 'ORG_GRADLE_PROJECT_SoloStudiosReleasesPassword',
usernameVariable: 'ORG_GRADLE_PROJECT_SoloStudiosReleasesUsername'
),
// TODO: does not yet exist (uncomment once added)
// usernamePassword(
// credentialsId: 'sonatype-maven-credentials',
// passwordVariable: 'ORG_GRADLE_PROJECT_SonatypePassword',
// usernameVariable: 'ORG_GRADLE_PROJECT_SonatypeUsername'
// ),
// usernamePassword(
// credentialsId: 'codemc-maven-credentials',
// passwordVariable: 'ORG_GRADLE_PROJECT_CodeMCPassword',
// usernameVariable: 'ORG_GRADLE_PROJECT_CodeMCUsername'
// )
]) {
withGradle {
sh './gradlew publish'
//sh './gradlew publishAllPublicationsToSoloStudiosReleasesRepository'
// sh './gradlew publishAllPublicationsToSonatypeRepository'
// sh './gradlew publishAllPublicationsToCodeMCRepository'
}
}
}
}
}
post {
always {
discoverReferenceBuild()
// junit testResults: '**/build/test-results/*/TEST-*.xml'
recordIssues(
aggregatingResults: true,
enabledForFailure: true,
minimumSeverity: 'ERROR',
sourceCodeEncoding: 'UTF-8',
checksAnnotationScope: 'ALL',
sourceCodeRetention: 'LAST_BUILD',
tools: [java(), javaDoc()]
)
discordSend(
title: env.JOB_NAME + ' ' + env.BUILD_DISPLAY_NAME,
showChangeset: true,
enableArtifactsList: true,
link: env.BUILD_URL,
result: currentBuild.currentResult,
customAvatarUrl: 'https://github.com/PolyhedralDev.png',
customUsername: 'Solo Studios Jenkins',
webhookURL: env.DISCORD_WEBHOOK_URL,
)
cleanWs()
}
}
}
+6 -6
View File
@@ -22,11 +22,11 @@ repositories {
dependencies {
//TODO Allow pulling from Versions.kt
implementation("com.gradleup.shadow", "shadow-gradle-plugin", "8.3.6")
implementation("com.gradleup.shadow", "shadow-gradle-plugin", "8.3.9")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "2.0.0-beta.17")
implementation("org.ow2.asm", "asm", "9.8")
implementation("org.ow2.asm", "asm-tree", "9.8")
implementation("com.dfsek.tectonic", "common", "4.2.1")
implementation("org.yaml", "snakeyaml", "2.4")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "2.0.0-beta.18")
implementation("org.ow2.asm", "asm", "9.9")
implementation("org.ow2.asm", "asm-tree", "9.9")
implementation("com.dfsek.tectonic", "common", "4.3.1")
implementation("org.yaml", "snakeyaml", "2.5")
}
@@ -13,6 +13,7 @@ import org.gradle.kotlin.dsl.getByName
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.plugins.ide.idea.model.IdeaModel
fun Project.configureCompilation() {
apply(plugin = "maven-publish")
@@ -21,6 +22,13 @@ fun Project.configureCompilation() {
apply(plugin = "idea")
apply<TectonicDocPlugin>()
configure<IdeaModel> {
module {
isDownloadJavadoc = true
isDownloadSources = true
}
}
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
@@ -57,15 +57,6 @@ fun Project.configureDependencies() {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "Sonatype Snapshots"
}
maven("https://repo.opencollab.dev/maven-releases/") {
name = "OpenCollab Releases"
}
maven("https://repo.opencollab.dev/maven-snapshots/") {
name = "OpenCollab Snapshots"
}
maven("https://storehouse.okaeri.eu/repository/maven-public/") {
name = "Okaeri"
}
maven("https://repo.onarandombox.com/multiverse-releases") {
name = "onarandombox"
}
+22 -4
View File
@@ -49,9 +49,19 @@ fun Project.configureDistribution() {
doFirst {
try {
file("${buildDir}/resources/main/packs/").deleteRecursively()
file("${buildDir}/resources/main/metapacks/").deleteRecursively()
val overworldPackUrl =
URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/Overworld.zip")
val reimagENDPackUrl =
URL("https://github.com/PolyhedralDev/ReimagEND/releases/download/" + Versions.Terra.reimagENDConfig + "/ReimagEND.zip")
val tartarusPackUrl =
URL("https://github.com/PolyhedralDev/Tartarus/releases/download/" + Versions.Terra.tartarusConfig + "/Tartarus.zip")
val defaultPackUrl =
URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
downloadPack(defaultPackUrl, project)
URL("https://github.com/PolyhedralDev/DefaultMetapack/releases/download/" + Versions.Terra.defaultConfig + "/default.zip")
downloadPack(overworldPackUrl, project)
downloadPack(reimagENDPackUrl, project)
downloadPack(tartarusPackUrl, project)
downloadPack(defaultPackUrl, project, true)
} catch (_: Exception) {
}
}
@@ -97,6 +107,13 @@ fun Project.configureDistribution() {
resources.computeIfAbsent("packs") { ArrayList() }.add(it.name)
}
val metaPacksDir = File("${project.buildDir}/resources/main/metapacks/")
metaPacksDir.walkTopDown().forEach {
if (it.isDirectory || !it.name.endsWith(".zip")) return@forEach
resources.computeIfAbsent("metapacks") { ArrayList() }.add(it.name)
}
val langDir = File("${project(":common:implementation").buildDir}/resources/main/lang/")
langDir.walkTopDown().forEach {
@@ -164,9 +181,10 @@ fun Project.configureDistribution() {
}
}
fun downloadPack(packUrl: URL, project: Project) {
fun downloadPack(packUrl: URL, project: Project, metapack: Boolean = false) {
val fileName = packUrl.file.substring(packUrl.file.lastIndexOf("/"))
val file = File("${project.buildDir}/resources/main/packs/${fileName}")
val resourceType = if (metapack) "metapacks" else "packs"
val file = File("${project.buildDir}/resources/main/${resourceType}/${fileName}")
file.parentFile.mkdirs()
file.outputStream().write(packUrl.readBytes())
}
+7 -6
View File
@@ -16,16 +16,17 @@ fun Project.configurePublishing() {
}
repositories {
val mavenUrl = "https://repo.codemc.io/repository/maven-releases/"
val mavenUrl = "https://maven.solo-studios.ca/releases/"
//val mavenSnapshotUrl = "https://repo.codemc.io/repository/maven-snapshots/"
maven(mavenUrl) {
val mavenUsername: String? by project
val mavenPassword: String? by project
if (mavenUsername != null && mavenPassword != null) {
val SoloStudiosReleasesUsername: String? by project
val SoloStudiosReleasesPassword: String? by project
if (SoloStudiosReleasesUsername != null && SoloStudiosReleasesPassword != null) {
credentials {
username = mavenUsername
password = mavenPassword
username = SoloStudiosReleasesUsername
password = SoloStudiosReleasesPassword
}
}
}
+4 -8
View File
@@ -1,25 +1,21 @@
import java.io.ByteArrayOutputStream
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.kotlin.dsl.support.serviceOf
var isPrerelease = false
fun Project.getGitHash(): String {
val stdout = ByteArrayOutputStream()
exec {
return providers.exec {
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout
}
return stdout.toString().trim()
}.standardOutput.asText.get().trim()
}
fun Project.gitClone(name: String) {
val stdout = ByteArrayOutputStream()
exec {
providers.exec {
commandLine = mutableListOf("git", "clone", name)
standardOutput = stdout
}
}
+39 -33
View File
@@ -1,36 +1,39 @@
object Versions {
object Terra {
const val overworldConfig = "v1.5.2"
const val overworldConfig = "latest"
const val reimagENDConfig = "latest"
const val tartarusConfig = "latest"
const val defaultConfig = "latest"
}
object Libraries {
const val tectonic = "4.2.1"
const val tectonic = "4.3.1"
const val paralithic = "2.0.1"
const val strata = "1.3.2"
const val seismic = "2.0.4"
const val seismic = "2.5.7"
const val cloud = "2.0.0"
const val caffeine = "3.2.1"
const val caffeine = "3.2.2"
const val slf4j = "2.0.17"
object Internal {
const val shadow = "8.3.6"
const val apacheText = "1.13.1"
const val apacheIO = "2.19.0"
const val guava = "33.4.8-jre"
const val asm = "9.8"
const val snakeYml = "2.4"
const val jetBrainsAnnotations = "26.0.2"
const val junit = "5.13.1"
const val shadow = "8.3.9"
const val apacheText = "1.14.0"
const val apacheIO = "2.20.0"
const val guava = "33.5.0-jre"
const val asm = "9.9"
const val snakeYml = "2.5"
const val jetBrainsAnnotations = "26.0.2-1"
const val junit = "6.0.0"
const val nbt = "6.1"
}
}
object Fabric {
const val fabricAPI = "0.133.14+${Mod.minecraft}"
const val cloud = "2.0.0-beta.11"
const val fabricAPI = "0.140.0+${Mod.minecraft}"
const val cloud = "2.0.0-beta.13"
}
//
// object Quilt {
@@ -39,14 +42,14 @@ object Versions {
// }
object Mod {
const val mixin = "0.15.5+mixin.0.8.7"
const val mixinExtras = "0.4.1"
const val mixin = "0.16.5+mixin.0.8.7"
const val mixinExtras = "0.5.0"
const val minecraft = "1.21.9"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.17.2"
const val minecraft = "1.21.11"
const val yarn = "$minecraft+build.3"
const val fabricLoader = "0.18.3"
const val architecuryLoom = "1.11.440"
const val architecturyLoom = "1.13.463"
const val architecturyPlugin = "3.4.162"
}
@@ -57,17 +60,17 @@ object Versions {
// }
object Bukkit {
const val minecraft = "1.21.9-rc1-R0.1"
const val paperBuild = "$minecraft-20250930.133904-13"
const val minecraft = "1.21.11-rc3"
const val nms = "$minecraft-R0.1"
const val paperBuild = "$nms-20251208.200020-2"
const val paper = paperBuild
const val paperLib = "1.0.8"
const val reflectionRemapper = "0.1.2"
const val reflectionRemapper = "0.1.3"
const val paperDevBundle = paperBuild
const val runPaper = "2.3.1"
const val runPaperMinecraft = "1.21.9"
const val paperWeight = "2.0.0-beta.17"
const val cloud = "2.0.0-beta.11"
const val multiverse = "5.0.2"
const val paperWeight = "2.0.0-beta.19"
const val cloud = "2.0.0-beta.12"
const val multiverse = "5.3.0"
}
//
@@ -78,18 +81,21 @@ object Versions {
// }
//
object CLI {
const val logback = "1.5.18"
const val logback = "1.5.19"
const val picocli = "4.7.7"
}
object Allay {
const val api = "0.4.1"
const val gson = "2.13.1"
const val mappings = "3626653"
const val mappingsGenerator = "366618e"
const val api = "0.13.0"
const val gson = "2.13.2"
const val mappings = "15398c1"
const val mappingsGenerator = "8fa6058"
const val mcmeta = "e85a17c"
}
object Minestom {
const val minestom = "1_21_6-a40d7115d4"
const val minestom = "2025.12.20c-1.21.11"
}
}
@@ -46,6 +46,6 @@ class BaseBiomeColumn implements Column<Biome> {
@Override
public Biome get(int y) {
return biomeProvider.extrude(base, x, y, z, seed);
return biomeProvider.pipeline.extrude(base, x, y, z, seed);
}
}
@@ -6,37 +6,33 @@ import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.utils.ExtrusionPipeline;
import com.dfsek.terra.addons.biome.extrusion.utils.ExtrusionPipelineFactory;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomeExtrusionProvider implements BiomeProvider {
public final ExtrusionPipeline pipeline;
private final BiomeProvider delegate;
private final Set<Biome> biomes;
private final Extrusion[] extrusions;
private final int resolution;
public BiomeExtrusionProvider(BiomeProvider delegate, List<Extrusion> extrusions, int resolution) {
this.delegate = delegate;
this.biomes = delegate.stream().collect(Collectors.toSet());
extrusions.forEach(e -> biomes.addAll(e.getBiomes()));
this.extrusions = extrusions.toArray(new Extrusion[0]);
this.pipeline = ExtrusionPipelineFactory.create(extrusions);
this.resolution = resolution;
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
Biome delegated = delegate.getBiome(x, y, z, seed);
return extrude(delegated, x, y, z, seed);
}
public Biome extrude(Biome original, int x, int y, int z, long seed) {
for(int i = 0; i < extrusions.length; i++) {
original = extrusions[i].extrude(original, x, y, z, seed);
}
return original;
return pipeline.extrude(delegated, x, y, z, seed);
}
@Override
@@ -64,4 +60,4 @@ public class BiomeExtrusionProvider implements BiomeProvider {
public BiomeProvider getDelegate() {
return delegate;
}
}
}
@@ -10,6 +10,7 @@ import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.addons.biome.query.api.BiomeQueries;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.collection.TriStateIntCache;
import com.dfsek.terra.api.util.range.Range;
import com.dfsek.terra.api.world.biome.Biome;
@@ -19,25 +20,41 @@ import com.dfsek.terra.api.world.biome.Biome;
*/
public class ReplaceExtrusion implements Extrusion {
private final Sampler sampler;
private final Range range;
private final ProbabilityCollection<ReplaceableBiome> biomes;
private final Predicate<Biome> hasTag;
private final TriStateIntCache cache;
public ReplaceExtrusion(Sampler sampler, Range range, ProbabilityCollection<ReplaceableBiome> biomes, String tag) {
this.sampler = sampler;
this.range = range;
this.biomes = biomes;
this.hasTag = BiomeQueries.has(tag);
this.cache = new TriStateIntCache(Biome.INT_ID_COUNTER.get());
}
@Override
public Biome extrude(Biome original, int x, int y, int z, long seed) {
if(hasTag.test(original)) {
return range.ifInRange(y, () -> biomes.get(sampler, x, y, z, seed).get(original), original);
int id = original.getIntID();
long state = cache.get(id);
boolean passes;
if(state == TriStateIntCache.STATE_UNSET) {
// Only run the test if unset in cache
passes = hasTag.test(original);
cache.set(id, passes);
} else {
// Read the primitive long directly
passes = (state == TriStateIntCache.STATE_TRUE);
}
if(passes) {
if(range.isInRange(y)) {
return biomes.get(sampler, x, y, z, seed).get(original);
}
}
return original;
}
@@ -0,0 +1,8 @@
package com.dfsek.terra.addons.biome.extrusion.utils;
import com.dfsek.terra.api.world.biome.Biome;
public interface ExtrusionPipeline {
Biome extrude(Biome original, int x, int y, int z, long seed);
}
@@ -0,0 +1,158 @@
package com.dfsek.terra.addons.biome.extrusion.utils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.api.world.biome.Biome;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;
import static org.objectweb.asm.Opcodes.V1_8;
public class ExtrusionPipelineFactory {
private static final AtomicInteger ID_COUNTER = new AtomicInteger(0);
// Type Descriptors
private static final String EXTRUSION_TYPE = Type.getInternalName(Extrusion.class);
private static final String EXTRUSION_DESC = Type.getDescriptor(Extrusion.class);
private static final String BIOME_DESC = Type.getDescriptor(Biome.class);
private static final String PIPELINE_INTERFACE = Type.getInternalName(ExtrusionPipeline.class);
// Method Signature: (Biome, int, int, int, long) -> Biome
private static final String EXTRUDE_SIG = "(" + BIOME_DESC + "IIIJ)" + BIOME_DESC;
public static ExtrusionPipeline create(List<Extrusion> extrusions) {
// Optimization: If empty, return identity
if(extrusions.isEmpty()) {
return (original, x, y, z, seed) -> original;
}
String className = "com/dfsek/terra/addons/biome/extrusion/GeneratedExtrusionPipeline_" + ID_COUNTER.getAndIncrement();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
// 1. Define Class
cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, className, null, "java/lang/Object", new String[]{ PIPELINE_INTERFACE });
// 2. Define Fields (e0, e1, e2...)
for(int i = 0; i < extrusions.size(); i++) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_FINAL, "e" + i, EXTRUSION_DESC, null, null);
fv.visitEnd();
}
// 3. Generate Constructor(Extrusion[])
generateConstructor(cw, className, extrusions.size());
// 4. Generate extrude() method
generateExtrudeMethod(cw, className, extrusions.size());
cw.visitEnd();
// 5. Load and Instantiate
byte[] bytecode = cw.toByteArray();
try {
Class<?> generatedClass = new PipelineClassLoader(ExtrusionPipelineFactory.class.getClassLoader())
.defineClass(className.replace('/', '.'), bytecode);
return (ExtrusionPipeline) generatedClass.getConstructor(Extrusion[].class)
.newInstance((Object) extrusions.toArray(new Extrusion[0]));
} catch(Exception e) {
throw new RuntimeException("Failed to generate ExtrusionPipeline", e);
}
}
private static void generateConstructor(ClassWriter cw, String className, int count) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "([L" + EXTRUSION_TYPE + ";)V", null, null);
mv.visitCode();
// super()
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
// Assign array elements to fields
for(int i = 0; i < count; i++) {
mv.visitVarInsn(ALOAD, 0); // Load this
mv.visitVarInsn(ALOAD, 1); // Load array argument
mv.visitIntInsn(SIPUSH, i); // Load index
mv.visitInsn(AALOAD); // Load array[i]
mv.visitFieldInsn(PUTFIELD, className, "e" + i, EXTRUSION_DESC);
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0); // Computed automatically
mv.visitEnd();
}
private static void generateExtrudeMethod(ClassWriter cw, String className, int count) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "extrude", EXTRUDE_SIG, null, null);
mv.visitCode();
// Helper var indices:
// 0: this
// 1: Biome original (We will update this or chain it on stack)
// 2: int x
// 3: int y
// 4: int z
// 5: long seed
mv.visitVarInsn(ALOAD, 1); // Load 'original' Biome onto stack initially
for(int i = 0; i < count; i++) {
// Stack contains: [CurrentBiome]
mv.visitVarInsn(ALOAD, 0); // Load 'this'
mv.visitFieldInsn(GETFIELD, className, "e" + i, EXTRUSION_DESC); // Load Extrusion field
// Stack: [CurrentBiome, Extrusion]
// We need: [Extrusion, CurrentBiome, x, y, z, seed]
mv.visitInsn(SWAP); // Swap to get [Extrusion, CurrentBiome]
mv.visitVarInsn(ILOAD, 2); // x
mv.visitVarInsn(ILOAD, 3); // y
mv.visitVarInsn(ILOAD, 4); // z
mv.visitVarInsn(LLOAD, 5); // seed
// Invoke Extrusion.extrude(Biome, x, y, z, seed)
mv.visitMethodInsn(INVOKEINTERFACE, EXTRUSION_TYPE, "extrude", EXTRUDE_SIG, true);
// Stack now contains: [NewBiome]
// Loop continues using this result as input for the next one
}
mv.visitInsn(ARETURN); // Return the final Biome
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// Custom ClassLoader to inject the bytes
private static class PipelineClassLoader extends ClassLoader {
public PipelineClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
}
@@ -73,12 +73,13 @@ public class BiomeChunkImpl implements BiomeChunk {
lookupArray = tempArray;
// Apply stage to working grid
ViewPoint viewPoint = new ViewPoint(this, gridInterval, lookupArray, size);
for(int gridZ = 0; gridZ < gridSize; gridZ = gridZ + 1) {
for(int gridX = 0; gridX < gridSize; gridX = gridX + 1) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[(xIndex * size) + zIndex] = stage.apply(
new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray, size));
viewPoint.set(gridX, gridZ, xIndex, zIndex);
biomes[(xIndex * size) + zIndex] = stage.apply(viewPoint);
}
}
}
@@ -157,25 +158,32 @@ public class BiomeChunkImpl implements BiomeChunk {
*/
public static class ViewPoint {
private final BiomeChunkImpl chunk;
private final PipelineBiome biome;
private PipelineBiome biome;
private final int gridInterval;
private final int gridX;
private final int gridZ;
private final int xIndex;
private final int zIndex;
private int gridX;
private int gridZ;
private int xIndex;
private int zIndex;
private final PipelineBiome[] lookupArray;
private final int size;
private ViewPoint(BiomeChunkImpl chunk, int gridInterval, int gridX, int gridZ, int xIndex, int zIndex,
private ViewPoint(BiomeChunkImpl chunk, int gridInterval,
PipelineBiome[] lookupArray, int size) {
this.chunk = chunk;
this.gridInterval = gridInterval;
this.gridX = 0;
this.gridZ = 0;
this.xIndex = 0;
this.zIndex = 0;
this.lookupArray = lookupArray;
this.size = size;
}
public void set(int gridX, int gridZ, int xIndex, int zIndex) {
this.gridX = gridX;
this.gridZ = gridZ;
this.xIndex = xIndex;
this.zIndex = zIndex;
this.lookupArray = lookupArray;
this.size = size;
this.biome = lookupArray[(this.xIndex * this.size) + this.zIndex];
}
@@ -0,0 +1,5 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
}
@@ -0,0 +1,131 @@
package com.dfsek.terra.addons.commands.locate;
import com.dfsek.seismic.type.vector.Vector2Int;
import com.dfsek.seismic.type.vector.Vector3Int;
import com.dfsek.terra.api.util.generic.either.Either;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.info.WorldProperties;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class BiomeLocator {
/**
* Locates the nearest biome matching the given predicate using a parallelized square spiral search.
*
* @param provider The BiomeProvider to search in.
* @param properties The world properties (needed for seed and height bounds).
* @param originX Starting X coordinate.
* @param originZ Starting Z coordinate.
* @param radius The maximum radius (in blocks) to search.
* @param step The search step/increment. Higher values are faster but less accurate.
* @param filter The condition to match the biome.
* @param search3D If true, searches the entire vertical column at each step. If false, only checks originY.
* @return An Optional containing the location of the found biome, or empty if not found.
*/
public static Optional<Either<Vector3Int, Vector2Int>> search(
@NotNull BiomeProvider provider,
@NotNull WorldProperties properties,
int originX,
int originZ,
int radius,
int step,
@NotNull Predicate<Biome> filter,
boolean search3D
) {
long seed = properties.getSeed();
int minHeight = properties.getMinHeight();
int maxHeight = properties.getMaxHeight();
// 1. Check the exact center first
Optional<Either<Vector3Int, Vector2Int>> centerResult = check(provider, seed, originX, originZ, step, filter, search3D, minHeight, maxHeight);
if (centerResult.isPresent()) {
return centerResult;
}
// 2. Begin Parallel Square Spiral Search
// We iterate rings sequentially to guarantee finding the *nearest* result.
// However, we process all points within a specific ring in parallel.
for (int r = step; r <= radius; r += step) {
final int currentRadius = r;
final int minX = -currentRadius;
final int maxX = currentRadius;
final int minZ = -currentRadius;
final int maxZ = currentRadius;
Stream<int[]> northSide = IntStream.iterate(minX, n -> n < maxX, n -> n + step)
.mapToObj(x -> new int[]{x, minZ}); // Fixed Z (min), varying X
Stream<int[]> eastSide = IntStream.iterate(minZ, n -> n < maxZ, n -> n + step)
.mapToObj(z -> new int[]{maxX, z}); // Fixed X (max), varying Z
Stream<int[]> southSide = IntStream.iterate(maxX, n -> n > minX, n -> n - step)
.mapToObj(x -> new int[]{x, maxZ}); // Fixed Z (max), varying X
Stream<int[]> westSide = IntStream.iterate(maxZ, n -> n > minZ, n -> n - step)
.mapToObj(z -> new int[]{minX, z}); // Fixed X (min), varying Z
Optional<Either<Vector3Int, Vector2Int>> ringResult = Stream.of(northSide, eastSide, southSide, westSide)
.flatMap(Function.identity())
.parallel()
.map(coords -> check(
provider,
seed,
originX + coords[0],
originZ + coords[1],
step,
filter,
search3D,
minHeight,
maxHeight
))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst(); // findFirst() respects encounter order (North -> East -> South -> West)
if (ringResult.isPresent()) {
return ringResult;
}
}
return Optional.empty();
}
/**
* Helper to check a specific coordinate column or point.
* This logic is executed inside the worker threads.
*/
private static Optional<Either<Vector3Int, Vector2Int>> check(
BiomeProvider provider,
long seed,
int x,
int z,
int step,
Predicate<Biome> filter,
boolean search3D,
int minHeight,
int maxHeight
) {
if (search3D) {
// Iterate from bottom to top of the world using the step
for (int y = minHeight; y < maxHeight; y += step) {
if (filter.test(provider.getBiome(x, y, z, seed))) {
return Optional.of(Either.left(Vector3Int.of(x, y, z)));
}
}
return Optional.empty();
} else {
// 2D Mode: Check only the base biome
// We use a flatMap approach here to be safe with Optionals inside the stream
return provider.getBaseBiome(x, z, seed)
.filter(filter)
.map(b -> Either.right(Vector2Int.of(x, z)));
}
}
}
@@ -0,0 +1,142 @@
package com.dfsek.terra.addons.commands.locate;
import com.dfsek.seismic.type.vector.Vector2Int;
import com.dfsek.seismic.type.vector.Vector3Int;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.component.DefaultValue;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.description.Description;
import org.incendo.cloud.parser.standard.IntegerParser;
import java.util.Optional;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.command.arguments.RegistryArgument;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.generic.either.Either;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.World;
import com.dfsek.terra.api.world.biome.Biome;
public class LocateCommandAddon implements AddonInitializer {
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
private static Registry<Biome> getBiomeRegistry(CommandContext<CommandSender> sender) {
return sender.sender().getEntity().orElseThrow().world().getPack().getRegistry(Biome.class);
}
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, CommandRegistrationEvent.class)
.then(event -> {
CommandManager<CommandSender> manager = event.getCommandManager();
manager.command(
manager.commandBuilder("search", Description.of("Locate things in the world"))
.literal("biome")
// Argument 1: The Biome to search for
.argument(RegistryArgument.builder("biome",
LocateCommandAddon::getBiomeRegistry,
TypeKey.of(Biome.class)))
// Argument 2: Radius (Optional, default 5000)
.optional("radius", IntegerParser.integerParser(100), DefaultValue.constant(5000))
// Argument 3: Step/Resolution (Optional, default 16)
.optional("step", IntegerParser.integerParser(1), DefaultValue.constant(16))
// Flag: Toggle 3D search (e.g., --3d or -3)
.flag(manager.flagBuilder("3d").withAliases("3").build())
// Flag: Auto resolution mode (e.g., --auto or -a)
.flag(manager.flagBuilder("auto").withAliases("a").build())
.handler(context -> {
// 1. Gather Context & Arguments
Biome targetBiome = context.get("biome");
Entity sender = context.sender().getEntity().orElseThrow(
() -> new Error("Only entities can run this command."));
World world = sender.world();
// Fetch properties needed for the locator
int radius = context.get("radius");
boolean search3D = context.flags().hasFlag("3d");
boolean autoMode = context.flags().hasFlag("auto");
// 2. Determine Initial Step
// If Auto: Start at radius / 2 (very coarse check).
// If Manual: Use provided step.
int stepArg = context.get("step");
int currentStep = autoMode ? Integer.highestOneBit(radius - 1) : stepArg;
// Notify player
String modeMsg = autoMode ? " (Auto Mode)" : " (Step: " + currentStep + ")";
context.sender().sendMessage(
"Searching for " + targetBiome.getID() + " within " + radius + " blocks" + modeMsg + "...");
Optional<Either<Vector3Int, Vector2Int>> result;
// 3. Execute Search Loop
while(true) {
result = BiomeLocator.search(
world.getBiomeProvider(),
world,
sender.position().getFloorX(),
sender.position().getFloorZ(),
radius,
currentStep,
found -> found.equals(targetBiome), // Match specific biome instance
search3D
);
// Exit Conditions:
// 1. Found a result
if(result.isPresent()) {
break;
}
// 2. Not in auto mode (only run once)
if(!autoMode) {
break;
}
// 3. We just ran a search at step arg and failed (lowest resolution)
if(currentStep <= stepArg) {
break;
}
// Reduce step for next iteration (Adaptive Search)
currentStep /= 2;
context.sender().sendMessage("No result found, refining search (Step: " + currentStep + ")...");
}
// 4. Handle Result
if(result.isPresent()) {
Either<Vector3Int, Vector2Int> location = result.get();
String coords;
if(location.hasLeft()) { // 3D Result
Vector3Int vec = location.getLeft().get();
coords = String.format("%d, %d, %d", vec.getX(), vec.getY(), vec.getZ());
} else { // 2D Result
Vector2Int vec = location.getRight().get();
coords = String.format("%d, ~, %d", vec.getX(), vec.getZ());
}
context.sender().sendMessage("Found " + targetBiome.getID() + " at [" + coords + "]");
} else {
context.sender().sendMessage("Could not find " + targetBiome.getID() + " within " + radius + " blocks.");
}
})
.permission("terra.locate.biome")
);
});
}
}
@@ -0,0 +1,12 @@
schema-version: 1
contributors:
- Terra contributors
id: command-locate
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.commands.locate.LocateCommandAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
@@ -8,6 +8,7 @@
package com.dfsek.terra.addons.biome;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import com.dfsek.terra.api.properties.Context;
import com.dfsek.terra.api.world.biome.Biome;
@@ -25,6 +26,7 @@ public class UserDefinedBiome implements Biome {
private final Set<String> tags;
private final Context context = new Context();
private final int intID;
public UserDefinedBiome(PlatformBiome vanilla, BiomeTemplate config) {
this.vanilla = vanilla;
@@ -32,6 +34,7 @@ public class UserDefinedBiome implements Biome {
this.config = config;
this.color = config.getColor();
this.tags = config.getTags();
this.intID = INT_ID_COUNTER.getAndIncrement();
tags.add("BIOME:" + id);
tags.add("ALL");
}
@@ -61,6 +64,11 @@ public class UserDefinedBiome implements Biome {
return tags;
}
@Override
public int getIntID() {
return intID;
}
@Override
public String getID() {
return id;
@@ -2,18 +2,11 @@ package com.dfsek.terra.addons.feature.distributor.distributors;
import com.dfsek.seismic.algorithms.hashing.HashingFunctions;
import com.dfsek.seismic.math.integer.IntegerFunctions;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.Distributor;
public class PaddedGridDistributor implements Distributor {
private final int width;
private final int cellWidth;
private final int salt;
public PaddedGridDistributor(int width, int padding, int salt) {
@@ -27,12 +20,26 @@ public class PaddedGridDistributor implements Distributor {
int cellX = Math.floorDiv(x, cellWidth);
int cellZ = Math.floorDiv(z, cellWidth);
RandomGenerator random = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(
(HashingFunctions.murmur64(IntegerFunctions.squash(cellX, cellZ)) ^ seed) + salt);
int localX = x - (cellX * cellWidth);
int localZ = z - (cellZ * cellWidth);
int pointX = random.nextInt(width) + cellX * cellWidth;
int pointZ = random.nextInt(width) + cellZ * cellWidth;
if (localX >= width || localZ >= width) {
return false;
}
return x == pointX && z == pointZ;
long hash = HashingFunctions.murmur64(IntegerFunctions.squash(cellX, cellZ)) ^ seed;
hash += salt;
hash = HashingFunctions.splitMix64(hash);
int targetX = (int) ((hash & 0x7FFFFFFFFFFFFFFFL) % width);
if (localX != targetX) {
return false;
}
hash = HashingFunctions.splitMix64(hash);
int targetZ = (int) ((hash & 0x7FFFFFFFFFFFFFFFL) % width);
return localZ == targetZ;
}
}
@@ -3,13 +3,13 @@ package com.dfsek.terra.addons.biome.pipeline.image.config.converter.mapping;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.Map;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
import com.dfsek.terra.addons.image.util.MapUtil;
import java.util.Map;
public class DefinedPipelineBiomeColorMappingTemplate implements ObjectTemplate<ColorMapping<PipelineBiome>> {
@@ -12,12 +12,13 @@ import java.util.function.Consumer;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.properties.Property;
import com.dfsek.terra.api.data.Extendable;
/**
* Contains basic data about a {@link BlockType} in the world
*/
public interface BlockState extends Handle {
public interface BlockState extends Handle, Extendable {
/**
* Whether this {@link BlockState} matches another.
@@ -0,0 +1,32 @@
package com.dfsek.terra.api.block.state;
import com.dfsek.terra.api.data.ExtendedData;
public interface BlockStateExtended extends BlockState {
/**
* Gets the BlockData.
*
* @return BlockData of this BlockStateExtended
*/
ExtendedData getData();
/**
* Sets the BlockData.
*
* @param data BlockData to set
*
* @return New BlockStateExtended with the given BlockData
*/
BlockStateExtended setData(ExtendedData data);
/**
* Gets the BlockState.
*
* @return Raw BlockState of this BlockStateExtended
*/
BlockState getState();
@Override
default boolean isExtended() { return true; }
}
@@ -0,0 +1,11 @@
package com.dfsek.terra.api.data;
public interface Extendable {
/**
* Get whether this BlockState is an extended state.
* Extended states are states that contain extra data not normally present in a BlockState.
*
* @return Whether this state is extended.
*/
default boolean isExtended() { return false; }
}
@@ -0,0 +1,8 @@
package com.dfsek.terra.api.data;
import com.dfsek.terra.api.Handle;
public interface ExtendedData extends Handle {
String toString();
}
@@ -8,7 +8,8 @@
package com.dfsek.terra.api.entity;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.data.Extendable;
public interface EntityType extends Handle {
public interface EntityType extends Handle, Extendable {
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.api.entity;
import com.dfsek.terra.api.data.ExtendedData;
public interface EntityTypeExtended extends EntityType {
/**
* Gets the BlockData.
*
* @return BlockData of this EntityTypeExtended
*/
ExtendedData getData();
/**
* Sets the BlockData.
*
* @param data BlockData to set
*
* @return New EntityTypeExtended with the given BlockData
*/
EntityTypeExtended setData(ExtendedData data);
/**
* Gets the EntityType.
*
* @return Raw EntityType of this EntityTypeExtended
*/
EntityType getType();
@Override
default boolean isExtended() { return true; }
}
@@ -7,14 +7,14 @@
package com.dfsek.terra.api.structure.configured;
import org.jetbrains.annotations.ApiStatus.Experimental;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.structure.StructureSpawn;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.range.Range;
import org.jetbrains.annotations.ApiStatus.Experimental;
@Experimental
public interface ConfiguredStructure extends StringIdentifiable {
@@ -0,0 +1,67 @@
package com.dfsek.terra.api.util.collection;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import com.dfsek.seismic.util.UnsafeUtils;
public class TriStateIntCache {
public static final long STATE_UNSET = 0L;
public static final long STATE_FALSE = 1L;
public static final long STATE_TRUE = 2L;
private static final long BIT_MASK = 3L;
private final long[] data;
private static final VarHandle ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(long[].class);
private static int getOptimalMaxKeys(int requestedKeys) {
// 192 keys fill the first cache line exactly (along with the 16-byte header)
if (requestedKeys <= 192) {
return 192;
}
// For every additional line, we fit 256 keys (64 bytes * 4 keys/byte)
// We calculate the overflow beyond 192, round up to the nearest 256, and add it back.
int overflow = requestedKeys - 192;
int chunks = (overflow + 255) >>> 8; // Fast ceil division by 256
return 192 + (chunks << 8); // chunks * 256
}
public TriStateIntCache(int maxKeySize) {
this.data = new long[(getOptimalMaxKeys(maxKeySize) + 31) >>> 5];
}
/**
* Checks the cache state without any allocation.
*
* @return STATE_UNSET (0), STATE_FALSE (1), or STATE_TRUE (2)
*/
public long get(int key) {
long offset = UnsafeUtils.LONG_ARRAY_BASE + ((long)(key >>> 5) << UnsafeUtils.LONG_ARRAY_SHIFT);
long currentWord = UnsafeUtils.UNSAFE.getLong(data, offset);
return (currentWord >>> ((key << 1) & 63)) & BIT_MASK;
}
/**
* Sets the value safely. Handles race conditions internally.
*/
public void set(int key, boolean value) {
int index = key >>> 5;
int shift = (key << 1) & 63;
long targetWord = (value ? STATE_TRUE : STATE_FALSE) << shift;
long current;
do {
current = (long) ARRAY_HANDLE.getVolatile(data, index);
if (((current >>> shift) & BIT_MASK) != STATE_UNSET) {
return;
}
} while (!ARRAY_HANDLE.compareAndSet(data, index, current, current | targetWord));
}
}
@@ -18,7 +18,7 @@ public class ConstantRange implements Range {
private int max;
public ConstantRange(int min, int max) {
if(min >= max) throw new IllegalArgumentException("Minimum must not be greater than or equal to maximum!");
if(min > max) throw new IllegalArgumentException("Minimum must not be greater than maximum!");
this.max = max;
this.min = min;
}
@@ -9,6 +9,7 @@ package com.dfsek.terra.api.world.biome;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import com.dfsek.terra.api.properties.PropertyHolder;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
@@ -18,6 +19,7 @@ import com.dfsek.terra.api.registry.key.StringIdentifiable;
* Represents a Terra biome
*/
public interface Biome extends PropertyHolder, StringIdentifiable {
AtomicInteger INT_ID_COUNTER = new AtomicInteger(0);
/**
* Gets the platform biome this custom biome delegates to.
@@ -39,4 +41,12 @@ public interface Biome extends PropertyHolder, StringIdentifiable {
* @return A {@link Set} of String tags this biome holds.
*/
Set<String> getTags();
/**
* Get the numeric ID of this biome, generated at registration time
*
* @return The numeric ID.
*/
int getIntID();
}
@@ -1,68 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.loaders.config;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import org.jetbrains.annotations.NotNull;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.AnnotatedType;
import java.nio.file.Files;
import java.util.concurrent.ConcurrentHashMap;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.properties.Properties;
/*
* @deprecated Use the Image and ImageLoader class provided by the library-image addon instead. This is subject to removal in v7.
*/
@Deprecated
public class BufferedImageLoader implements TypeLoader<BufferedImage> {
private final ConfigPack pack;
public BufferedImageLoader(ConfigPack pack) {
this.pack = pack;
if(!pack.getContext().has(ImageCache.class))
pack.getContext().put(new ImageCache(new ConcurrentHashMap<>()));
}
@Override
public BufferedImage load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
throws LoadException {
return pack.getContext().get(ImageCache.class).map.computeIfAbsent((String) c, s -> {
try {
return ImageIO.read(Files.newInputStream(pack.getRootPath().resolve(s)));
} catch(IOException e) {
throw new LoadException("Unable to load image", e, depthTracker);
}
});
}
/*
* Cache prevents configs from loading the same image multiple times into memory
*/
private record ImageCache(ConcurrentHashMap<String, BufferedImage> map) implements Properties {
}
}
@@ -33,7 +33,6 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@@ -77,7 +76,6 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import com.dfsek.terra.api.world.chunk.generation.util.provider.ChunkGeneratorProvider;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.config.loaders.config.BufferedImageLoader;
import com.dfsek.terra.config.preprocessor.MetaListLikePreprocessor;
import com.dfsek.terra.config.preprocessor.MetaMapPreprocessor;
import com.dfsek.terra.config.preprocessor.MetaNumberPreprocessor;
@@ -273,8 +271,7 @@ public class ConfigPackImpl implements ConfigPack {
@Override
public void register(TypeRegistry registry) {
registry.registerLoader(ConfigType.class, configTypeRegistry)
.registerLoader(BufferedImage.class, new BufferedImageLoader(this));
registry.registerLoader(ConfigType.class, configTypeRegistry);
registryMap.forEach(registry::registerLoader);
shortcuts.forEach(registry::registerLoader); // overwrite with delegated shortcuts if present
}
@@ -31,6 +31,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -51,11 +52,12 @@ import com.dfsek.terra.api.util.reflection.TypeKey;
public class OpenRegistryImpl<T> implements OpenRegistry<T> {
private static final Entry<?> NULL = new Entry<>(null);
private final Map<RegistryKey, Entry<T>> objects;
private final ListMultimap<String, Pair<RegistryKey, Entry<T>>> objectIDs = Multimaps.newListMultimap(new HashMap<>(), ArrayList::new);
private final ListMultimap<String, Pair<RegistryKey, Entry<T>>> objectIDs = Multimaps.newListMultimap(new ConcurrentHashMap<>(),
ArrayList::new);
private final TypeKey<T> typeKey;
public OpenRegistryImpl(TypeKey<T> typeKey) {
this(new HashMap<>(), typeKey);
this(new ConcurrentHashMap<>(), typeKey);
}
protected OpenRegistryImpl(Map<RegistryKey, Entry<T>> init, TypeKey<T> typeKey) {
@@ -21,8 +21,8 @@ import java.io.IOException;
import java.io.Serial;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
import com.dfsek.terra.api.Platform;
@@ -41,12 +41,12 @@ public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
super(TypeKey.of(ConfigPack.class));
}
public void loadAll(Platform platform) throws IOException, PackLoadFailuresException {
public synchronized void loadAll(Platform platform) throws IOException, PackLoadFailuresException {
Path packsDirectory = platform.getDataFolder().toPath().resolve("packs");
Files.createDirectories(packsDirectory);
List<Exception> failedLoads = new ArrayList<>();
List<Exception> failedLoads = new CopyOnWriteArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
packs.parallel().forEach(path -> {
try {
ConfigPack pack = new ConfigPackImpl(path, platform);
registerChecked(pack.getRegistryKey(), pack);
@@ -19,4 +19,5 @@ script:
max-recursion: 1000
ignored-resources:
# - "addons"
# - "packs"
# - "packs"
# - "metapacks"
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
distributionSha256Sum=72f44c9f8ebcb1af43838f45ee5c4aa9c5444898b3468ab3f4af7b6076c5bc3f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -4
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 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.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
Vendored
+1 -2
View File
@@ -70,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+4 -4
View File
@@ -2,8 +2,8 @@
## Resource files
Current mapping version: je 1.21.4 to be 1.21.50
Current mapping version: je 1.21.9 to be 1.21.111
- `mapping/biomes.json` and `mapping/items.json` obtain from [GeyserMC/mappings](https://github.com/GeyserMC/mappings).
- `mapping/blocks.json` generated by using [GeyserMC/mappings-generator](https://github.com/GeyserMC/mappings-generator), and it's origin name is `generator_blocks.json`.
- `je_block_default_states.json` converted from [Block state values](https://zh.minecraft.wiki/w/Module:Block_state_values).
- `mapping/biomes.json` and `mapping/items.json` are obtained from [GeyserMC/mappings](https://github.com/GeyserMC/mappings).
- `mapping/blocks.json` is obtained from [GeyserMC/mappings-generator](https://github.com/GeyserMC/mappings-generator) (path: `https://github.com/GeyserMC/mappings-generator/blob/master/generator_blocks.json`).
- `je_blocks.json` is obtained from [misode/mcmeta](https://github.com/misode/mcmeta) (path: `https://github.com/misode/mcmeta/blob/<version>-summary/blocks/data.json`).
@@ -15,6 +15,10 @@ val geyserMappings: Configuration by configurations.register("geyserMappings") {
isCanBeConsumed = false
}
val mcmeta: Configuration by configurations.register("mcmeta") {
isCanBeConsumed = false
}
dependencies {
shadedApi(project(":common:implementation:base"))
@@ -25,6 +29,8 @@ dependencies {
geyserMappings("GeyserMC.mappings", "items", Versions.Allay.mappings, ext = "json")
geyserMappings("GeyserMC.mappings", "biomes", Versions.Allay.mappings, ext = "json")
geyserMappings("GeyserMC.mappings-generator", "generator_blocks", Versions.Allay.mappingsGenerator, ext = "json")
mcmeta("misode.mcmeta", "blocks/data", Versions.Allay.mcmeta, ext = "json")
}
tasks.processResources {
@@ -34,4 +40,7 @@ tasks.processResources {
// rather jank, but whatever
rename("(?:generator_)?([^-]+)-(.*)\\.json", "$1.json")
}
from(mcmeta) {
rename("data-(.*)\\.json", "je_blocks.json")
}
}
@@ -3,8 +3,8 @@ package com.dfsek.terra.allay;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.server.Server;
import org.allaymc.api.world.biome.BiomeId;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -45,7 +45,7 @@ public class AllayPlatform extends AbstractPlatform {
getConfigRegistry().get(wrapper.getConfigPack().getRegistryKey()).ifPresent(pack -> {
wrapper.setConfigPack(pack);
var dimension = wrapper.getAllayWorldGenerator().getDimension();
TerraAllayPlugin.INSTANCE.getPluginLogger().info(
TerraAllayPlugin.instance.getPluginLogger().info(
"Replaced pack in chunk generator for world {}",
dimension.getWorld().getWorldData().getDisplayName() + ":" + dimension.getDimensionInfo().dimensionId()
);
@@ -72,7 +72,7 @@ public class AllayPlatform extends AbstractPlatform {
@Override
public @NotNull File getDataFolder() {
return TerraAllayPlugin.INSTANCE.getPluginContainer().dataFolder().toFile();
return TerraAllayPlugin.instance.getPluginContainer().dataFolder().toFile();
}
@Override
@@ -89,6 +89,6 @@ public class AllayPlatform extends AbstractPlatform {
protected AllayBiome parseBiome(String id, DepthTracker depthTracker) throws LoadException {
if(!id.startsWith("minecraft:")) throw new LoadException("Invalid biome identifier " + id, depthTracker);
return new AllayBiome(BiomeId.fromId(Mapping.biomeIdJeToBe(id)));
return new AllayBiome(Registries.BIOMES.getByK1(Mapping.biomeIdJeToBe(id)));
}
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.allay;
import org.allaymc.api.utils.HashUtils;
import org.allaymc.api.utils.hash.HashUtils;
import java.util.Map;
import java.util.TreeMap;
@@ -10,12 +10,25 @@ import java.util.TreeMap;
* @author daoge_cmd
*/
public class JeBlockState {
protected final String identifier;
protected final TreeMap<String, String> properties;
protected int hash = Integer.MAX_VALUE;
private JeBlockState(String data) {
String[] strings = data.replace("[", ",").replace("]", ",").replace(" ", "").split(",");
// TODO: support block state with nbt (identifier[properties]{nbt}), for now we just ignore it
int braceIndex = data.indexOf('{');
if(braceIndex != -1) {
data = data.substring(0, braceIndex);
}
String[] strings = data
.replace("[", ",")
.replace("]", ",")
.replace(" ", "")
.split(",");
this.identifier = strings[0];
this.properties = new TreeMap<>();
if(strings.length > 1) {
@@ -8,19 +8,17 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockStateSafeGetter;
import org.allaymc.api.block.type.BlockStateGetter;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.item.type.ItemType;
import org.allaymc.api.item.type.ItemTypeSafeGetter;
import org.allaymc.api.item.type.ItemTypeGetter;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -37,11 +35,11 @@ public final class Mapping {
.registerTypeAdapterFactory(new IgnoreFailureTypeAdapterFactory())
.create();
private static final Map<String, Map<String, String>> JE_BLOCK_DEFAULT_PROPERTIES = new Object2ObjectOpenHashMap<>();
private static final Map<BlockState, JeBlockState> BE_BLOCK_STATE_TO_JE = new Object2ObjectOpenHashMap<>();
private static final Map<Integer, BlockState> JE_BLOCK_STATE_HASH_TO_BE = new Int2ObjectOpenHashMap<>();
private static final Map<String, ItemType<?>> JE_ITEM_ID_TO_BE = new Object2ObjectOpenHashMap<>();
private static final Map<String, Integer> JE_BIOME_ID_TO_BE = new Object2IntOpenHashMap<>();
private static final Map<String, Map<String, String>> JE_BLOCK_DEFAULT_PROPERTIES = new HashMap<>();
private static final Map<BlockState, JeBlockState> BE_BLOCK_STATE_TO_JE = new HashMap<>();
private static final Map<Integer, BlockState> JE_BLOCK_STATE_HASH_TO_BE = new HashMap<>();
private static final Map<String, ItemType<?>> JE_ITEM_ID_TO_BE = new HashMap<>();
private static final Map<String, Integer> JE_BIOME_ID_TO_BE = new HashMap<>();
private static final BlockState BE_AIR_STATE = BlockTypes.AIR.getDefaultState();
@@ -59,7 +57,7 @@ public final class Mapping {
public static BlockState blockStateJeToBe(JeBlockState jeBlockState) {
BlockState result = JE_BLOCK_STATE_HASH_TO_BE.get(jeBlockState.getHash());
if(result == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find be block state for {}", jeBlockState);
TerraAllayPlugin.instance.getPluginLogger().warn("Failed to find be block state for {}", jeBlockState);
return BE_AIR_STATE;
}
return result;
@@ -83,10 +81,19 @@ public final class Mapping {
return JE_BIOME_ID_TO_BE.get(jeBiomeId);
}
public static String dimensionIdBeToJe(String beDimensionId) {
return switch(beDimensionId) {
case "overworld" -> "minecraft:overworld";
case "nether" -> "minecraft:the_nether";
case "the_end" -> "minecraft:the_end";
default -> beDimensionId;
};
}
public static Map<String, String> getJeBlockDefaultProperties(String jeBlockIdentifier) {
var defaultProperties = JE_BLOCK_DEFAULT_PROPERTIES.get(jeBlockIdentifier);
if(defaultProperties == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find default properties for {}", jeBlockIdentifier);
TerraAllayPlugin.instance.getPluginLogger().warn("Failed to find default properties for {}", jeBlockIdentifier);
return Map.of();
}
@@ -100,7 +107,7 @@ public final class Mapping {
private static boolean initBiomeMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/biomes.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("biomes mapping not found");
TerraAllayPlugin.instance.getPluginLogger().error("biomes mapping not found");
return false;
}
@@ -108,7 +115,7 @@ public final class Mapping {
});
mappings.forEach((javaId, mapping) -> JE_BIOME_ID_TO_BE.put(javaId, mapping.bedrockId()));
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load biomes mapping", e);
TerraAllayPlugin.instance.getPluginLogger().error("Failed to load biomes mapping", e);
return false;
}
return true;
@@ -117,21 +124,21 @@ public final class Mapping {
private static boolean initItemMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/items.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("items mapping not found");
TerraAllayPlugin.instance.getPluginLogger().error("items mapping not found");
return false;
}
Map<String, ItemMapping> mappings = from(stream, new TypeToken<>() {
});
mappings.forEach((javaId, mapping) -> {
ItemType<?> itemType = ItemTypeSafeGetter
ItemType<?> itemType = ItemTypeGetter
.name(mapping.bedrockId())
.meta(mapping.bedrockData())
.itemType();
JE_ITEM_ID_TO_BE.put(javaId, itemType);
});
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load items mapping", e);
TerraAllayPlugin.instance.getPluginLogger().error("Failed to load items mapping", e);
return false;
}
return true;
@@ -140,7 +147,7 @@ public final class Mapping {
private static boolean initBlockStateMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/blocks.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("blocks mapping not found");
TerraAllayPlugin.instance.getPluginLogger().error("blocks mapping not found");
return false;
}
@@ -154,22 +161,28 @@ public final class Mapping {
JE_BLOCK_STATE_HASH_TO_BE.put(jeState.getHash(), beState);
});
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load blocks mapping", e);
TerraAllayPlugin.instance.getPluginLogger().error("Failed to load blocks mapping", e);
return false;
}
return true;
}
@SuppressWarnings("unchecked")
private static boolean initJeBlockDefaultProperties() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("je_block_default_states.json")) {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("je_blocks.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("je_block_default_states.json not found");
TerraAllayPlugin.instance.getPluginLogger().error("je_block_default_states.json not found");
return false;
}
Map<String, Map<String, String>> states = from(stream, new TypeToken<>() {
Map<String, List<Map<String, ?>>> data = from(stream, new TypeToken<>() {
});
JE_BLOCK_DEFAULT_PROPERTIES.putAll(states);
for(var entry : data.entrySet()) {
JE_BLOCK_DEFAULT_PROPERTIES.put(
"minecraft:" + entry.getKey(),
(Map<String, String>) entry.getValue().get(1)
);
}
} catch(IOException e) {
throw new RuntimeException(e);
}
@@ -182,7 +195,7 @@ public final class Mapping {
}
private static BlockState createBeBlockState(BlockMapping.BedrockState state) {
BlockStateSafeGetter.Getter getter = BlockStateSafeGetter.name("minecraft:" + state.bedrockId());
BlockStateGetter.Getter getter = BlockStateGetter.name("minecraft:" + state.bedrockId());
if(state.state() != null) {
convertValueType(state.state()).forEach(getter::property);
}
@@ -15,38 +15,38 @@ import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
*/
public class TerraAllayPlugin extends Plugin {
public static TerraAllayPlugin INSTANCE;
public static AllayPlatform PLATFORM;
public static TerraAllayPlugin instance;
public static AllayPlatform platform;
{
INSTANCE = this;
TerraAllayPlugin.instance = this;
}
@Override
public void onLoad() {
pluginLogger.info("Starting Terra...");
this.pluginLogger.info("Starting Terra...");
pluginLogger.info("Loading mapping...");
this.pluginLogger.info("Loading mapping...");
Mapping.init();
pluginLogger.info("Initializing allay platform...");
PLATFORM = new AllayPlatform();
PLATFORM.getEventManager().callEvent(new PlatformInitializationEvent());
this.pluginLogger.info("Initializing allay platform...");
TerraAllayPlugin.platform = new AllayPlatform();
TerraAllayPlugin.platform.getEventManager().callEvent(new PlatformInitializationEvent());
// TODO: adapt command manager
pluginLogger.info("Registering generator...");
this.pluginLogger.info("Registering generator...");
Registries.WORLD_GENERATOR_FACTORIES.register("TERRA", preset -> {
try {
AllayGeneratorWrapper wrapper = new AllayGeneratorWrapper(preset);
AllayPlatform.GENERATOR_WRAPPERS.add(wrapper);
return wrapper.getAllayWorldGenerator();
} catch(IllegalArgumentException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Fail to create world generator with preset: {}", preset, e);
TerraAllayPlugin.instance.getPluginLogger().error("Fail to create world generator with preset: {}", preset, e);
return Registries.WORLD_GENERATOR_FACTORIES.get("FLAT").apply("");
}
});
pluginLogger.info("Terra started");
this.pluginLogger.info("Terra started");
}
@Override
@@ -61,10 +61,10 @@ public class TerraAllayPlugin extends Plugin {
@Override
public void reload() {
if(PLATFORM.reload()) {
pluginLogger.info("Terra reloaded successfully.");
if(TerraAllayPlugin.platform.reload()) {
this.pluginLogger.info("Terra reloaded successfully.");
} else {
pluginLogger.error("Terra failed to reload.");
this.pluginLogger.error("Terra failed to reload.");
}
}
@@ -0,0 +1,51 @@
package com.dfsek.terra.allay.delegate;
import com.dfsek.seismic.type.vector.Vector3;
import org.allaymc.api.blockentity.BlockEntity;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.block.state.BlockState;
/**
* @author daoge_cmd
*/
public record AllayBlockEntity(BlockEntity allayBlockEntity) implements com.dfsek.terra.api.block.entity.BlockEntity {
@Override
public boolean update(boolean applyPhysics) {
return false;
}
@Override
public Vector3 getPosition() {
var pos = this.allayBlockEntity.getPosition();
return Vector3.of(pos.x(), pos.y(), pos.z());
}
@Override
public int getX() {
return this.allayBlockEntity.getPosition().x();
}
@Override
public int getY() {
return this.allayBlockEntity.getPosition().y();
}
@Override
public int getZ() {
return this.allayBlockEntity.getPosition().z();
}
@Override
public BlockState getBlockState() {
var allayBlockState = this.allayBlockEntity.getBlockState();
return new AllayBlockState(allayBlockState, Mapping.blockStateBeToJe(this.allayBlockEntity.getBlockState()));
}
@Override
public Object getHandle() {
return this.allayBlockEntity;
}
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.data.BlockTags;
import org.allaymc.api.block.type.BlockType;
import com.dfsek.terra.allay.Mapping;
@@ -1,7 +1,7 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.data.BlockTags;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.chunk.Chunk;
import org.jetbrains.annotations.NotNull;
@@ -21,6 +21,13 @@ public record AllayChunk(ServerWorld world, Chunk allayChunk) implements com.dfs
@Override
public void setBlock(int x, int y, int z, BlockState data, boolean physics) {
var dimensionInfo = allayChunk.getDimensionInfo();
if(x < 0 || x > 15 ||
z < 0 || z > 15 ||
y < dimensionInfo.minHeight() || y > dimensionInfo.maxHeight()) {
return;
}
AllayBlockState allayBlockState = (AllayBlockState) data;
allayChunk.setBlockState(x, y, z, allayBlockState.allayBlockState());
if(allayBlockState.containsWater() || allayChunk.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
@@ -7,7 +7,7 @@ import com.dfsek.terra.api.world.ServerWorld;
/**
* NOTICE: Entity is not supported currently, and this is a fake implementation.
* TODO: Entity is not supported currently, and this is a fake implementation.
*
* @author daoge_cmd
*/
@@ -1,7 +1,7 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.data.BlockTags;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.chunk.UnsafeChunk;
import org.jetbrains.annotations.NotNull;
@@ -26,6 +26,13 @@ public record AllayProtoChunk(UnsafeChunk allayChunk) implements ProtoChunk {
@Override
public void setBlock(int x, int y, int z, @NotNull BlockState blockState) {
var dimensionInfo = allayChunk.getDimensionInfo();
if(x < 0 || x > 15 ||
z < 0 || z > 15 ||
y < dimensionInfo.minHeight() || y > dimensionInfo.maxHeight()) {
return;
}
AllayBlockState allayBlockState = (AllayBlockState) blockState;
allayChunk.setBlockState(x, y, z, allayBlockState.allayBlockState());
if(allayBlockState.containsWater() || allayChunk.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
@@ -1,8 +1,8 @@
package com.dfsek.terra.allay.delegate;
import com.dfsek.seismic.type.vector.Vector3;
import org.allaymc.api.block.data.BlockTags;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.generator.context.OtherChunkAccessibleContext;
@@ -26,6 +26,23 @@ public record AllayProtoWorld(AllayServerWorld allayServerWorld, OtherChunkAcces
private static final org.allaymc.api.block.type.BlockState WATER = BlockTypes.WATER.ofState(
BlockPropertyTypes.LIQUID_DEPTH.createValue(0));
// TODO: use method in OtherChunkAccessibleContext directly after bumped allay-api version to 0.14.0
private static org.allaymc.api.blockentity.BlockEntity getBlockEntity(OtherChunkAccessibleContext context, int x, int y, int z) {
var currentChunk = context.getCurrentChunk();
var currentChunkX = currentChunk.getX();
var currentChunkZ = currentChunk.getZ();
var dimInfo = currentChunk.getDimensionInfo();
if(x >= currentChunkX * 16 && x < currentChunkX * 16 + 16 &&
z >= currentChunkZ * 16 && z < currentChunkZ * 16 + 16 &&
y >= dimInfo.minHeight() && y <= dimInfo.maxHeight()) {
return currentChunk.getBlockEntity(x & 15, y, z & 15);
} else {
var chunk = context.getChunkSource().getChunk(x >> 4, z >> 4);
return chunk == null ? null : chunk.getBlockEntity(x & 15, y, z & 15);
}
}
@Override
public int centerChunkX() {
return context.getCurrentChunk().getX();
@@ -43,6 +60,11 @@ public record AllayProtoWorld(AllayServerWorld allayServerWorld, OtherChunkAcces
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
var dimensionInfo = allayServerWorld.allayDimension().getDimensionInfo();
if(y < dimensionInfo.minHeight() || y > dimensionInfo.maxHeight()) {
return;
}
AllayBlockState allayBlockState = (AllayBlockState) data;
context.setBlockState(x, y, z, allayBlockState.allayBlockState());
if(allayBlockState.containsWater() || context.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
@@ -63,7 +85,7 @@ public record AllayProtoWorld(AllayServerWorld allayServerWorld, OtherChunkAcces
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return null;
return new AllayBlockEntity(getBlockEntity(context, x, y, z));
}
@Override
@@ -22,11 +22,16 @@ import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
public record AllayServerWorld(AllayGeneratorWrapper allayGeneratorWrapper, Dimension allayDimension) implements ServerWorld {
@Override
public Chunk getChunkAt(int x, int z) {
return new AllayChunk(this, allayDimension.getChunkService().getChunk(x, z));
return new AllayChunk(this, allayDimension.getChunkManager().getChunk(x, z));
}
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
var dimensionInfo = allayDimension.getDimensionInfo();
if(y < dimensionInfo.minHeight() || y > dimensionInfo.maxHeight()) {
return;
}
// In dimension#setBlockState() method, Water will be moved to layer 1 if it is placed at layer 0
allayDimension.setBlockState(x, y, z, ((AllayBlockState) data).allayBlockState());
}
@@ -44,7 +49,7 @@ public record AllayServerWorld(AllayGeneratorWrapper allayGeneratorWrapper, Dime
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return null;
return new AllayBlockEntity(allayDimension.getBlockEntity(x, y, z));
}
@Override
@@ -0,0 +1,42 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.world.data.DimensionInfo;
import com.dfsek.terra.api.world.info.WorldProperties;
/**
* @author daoge_cmd
*/
public class AllayWorldProperties implements WorldProperties {
private final Object fakeHandle;
private final long seed;
private final DimensionInfo dimensionInfo;
public AllayWorldProperties(long seed, DimensionInfo dimensionInfo) {
this.fakeHandle = new Object();
this.seed = seed;
this.dimensionInfo = dimensionInfo;
}
@Override
public long getSeed() {
return this.seed;
}
@Override
public int getMaxHeight() {
return dimensionInfo.maxHeight();
}
@Override
public int getMinHeight() {
return dimensionInfo.minHeight();
}
@Override
public Object getHandle() {
return fakeHandle;
}
}
@@ -1,22 +1,22 @@
package com.dfsek.terra.allay.generator;
import com.google.common.base.Preconditions;
import org.allaymc.api.utils.AllayStringUtils;
import org.allaymc.api.world.biome.BiomeType;
import org.allaymc.api.world.chunk.UnsafeChunk;
import org.allaymc.api.world.data.DimensionInfo;
import org.allaymc.api.world.generator.WorldGenerator;
import org.allaymc.api.world.generator.context.NoiseContext;
import org.allaymc.api.world.generator.context.PopulateContext;
import org.allaymc.api.world.generator.function.Noiser;
import org.allaymc.api.world.generator.function.Populator;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.allay.TerraAllayPlugin;
import com.dfsek.terra.allay.delegate.AllayProtoChunk;
import com.dfsek.terra.allay.delegate.AllayProtoWorld;
import com.dfsek.terra.allay.delegate.AllayServerWorld;
import com.dfsek.terra.allay.delegate.AllayWorldProperties;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
@@ -30,27 +30,23 @@ import com.dfsek.terra.api.world.info.WorldProperties;
*/
public class AllayGeneratorWrapper implements GeneratorWrapper {
protected static final String OPTION_META_PACK_NAME = "meta-pack";
protected static final String OPTION_PACK_NAME = "pack";
protected static final String OPTION_SEED = "seed";
protected final BiomeProvider biomeProvider;
protected final long seed;
protected final WorldGenerator allayWorldGenerator;
protected ChunkGenerator chunkGenerator;
protected ConfigPack configPack;
protected ChunkGenerator chunkGenerator;
protected BiomeProvider biomeProvider;
protected WorldProperties worldProperties;
protected AllayServerWorld allayServerWorld;
public AllayGeneratorWrapper(String preset) {
Map<String, String> options = AllayStringUtils.parseOptions(preset);
String packName = options.get(OPTION_PACK_NAME);
if(packName == null) {
throw new IllegalArgumentException("Missing config pack name");
}
this.seed = Long.parseLong(options.getOrDefault(OPTION_SEED, "0"));
this.configPack = getConfigPack(packName);
this.chunkGenerator = createGenerator(this.configPack);
this.biomeProvider = this.configPack.getBiomeProvider();
var options = AllayStringUtils.parseOptions(preset);
this.seed = parseSeed(options.get(OPTION_SEED));
this.allayWorldGenerator = WorldGenerator
.builder()
.name("TERRA")
@@ -59,40 +55,52 @@ public class AllayGeneratorWrapper implements GeneratorWrapper {
.populators(new AllayPopulator())
.onDimensionSet(dimension -> {
this.allayServerWorld = new AllayServerWorld(this, dimension);
this.worldProperties = new WorldProperties() {
this.worldProperties = new AllayWorldProperties(this.seed, dimension.getDimensionInfo());
private final Object fakeHandle = new Object();
var metaPackName = options.get(OPTION_META_PACK_NAME);
if(metaPackName != null) {
setConfigPack(getConfigPackByMeta(metaPackName, dimension.getDimensionInfo()));
return;
}
@Override
public long getSeed() {
return seed;
}
var packName = options.get(OPTION_PACK_NAME);
if(packName != null) {
setConfigPack(getConfigPackById(packName));
return;
}
@Override
public int getMaxHeight() {
return dimension.getDimensionInfo().maxHeight();
}
@Override
public int getMinHeight() {
return dimension.getDimensionInfo().minHeight();
}
@Override
public Object getHandle() {
return fakeHandle;
}
};
throw new IllegalArgumentException("Either 'pack' or 'meta-pack' option should be specified in the generator preset!");
})
.build();
}
protected static ConfigPack getConfigPack(String packName) {
Optional<ConfigPack> byId = TerraAllayPlugin.PLATFORM.getConfigRegistry().getByID(packName);
return byId.orElseGet(
() -> TerraAllayPlugin.PLATFORM.getConfigRegistry().getByID(packName.toUpperCase(Locale.ENGLISH))
.orElseThrow(() -> new IllegalArgumentException("Cant find terra config pack named " + packName))
);
protected static long parseSeed(String str) {
if(str == null) {
return 0;
}
try {
return Long.parseLong(str);
} catch(NumberFormatException e) {
// Return the hashcode of the string if it cannot be parsed to a long value directly
return str.hashCode();
}
}
protected static ConfigPack getConfigPackById(String packId) {
return TerraAllayPlugin.platform
.getConfigRegistry()
.getByID(packId)
.orElseThrow(() -> new IllegalArgumentException("Cant find terra config pack named " + packId));
}
protected static ConfigPack getConfigPackByMeta(String metaPackId, DimensionInfo dimensionInfo) {
return TerraAllayPlugin.platform
.getMetaConfigRegistry()
.getByID(metaPackId)
.orElseThrow(() -> new IllegalArgumentException("Cant find terra meta pack named " + metaPackId))
.packs()
.get(Mapping.dimensionIdBeToJe(dimensionInfo.toString()));
}
protected static ChunkGenerator createGenerator(ConfigPack configPack) {
@@ -101,7 +109,7 @@ public class AllayGeneratorWrapper implements GeneratorWrapper {
@Override
public ChunkGenerator getHandle() {
return chunkGenerator;
return this.chunkGenerator;
}
public BiomeProvider getBiomeProvider() {
@@ -113,8 +121,10 @@ public class AllayGeneratorWrapper implements GeneratorWrapper {
}
public void setConfigPack(ConfigPack configPack) {
Preconditions.checkNotNull(configPack, "Config pack cannot be null!");
this.configPack = configPack;
this.chunkGenerator = createGenerator(this.configPack);
this.biomeProvider = this.configPack.getBiomeProvider();
}
public long getSeed() {
@@ -152,11 +162,6 @@ public class AllayGeneratorWrapper implements GeneratorWrapper {
}
return true;
}
@Override
public String getName() {
return "TERRA_NOISER";
}
}
@@ -170,14 +175,9 @@ public class AllayGeneratorWrapper implements GeneratorWrapper {
generationStage.populate(tmp);
}
} catch(Exception e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Error while populating chunk", e);
TerraAllayPlugin.instance.getPluginLogger().error("Error while populating chunk", e);
}
return true;
}
@Override
public String getName() {
return "TERRA_POPULATOR";
}
}
}
@@ -1,7 +1,7 @@
package com.dfsek.terra.allay.handle;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.utils.Identifier;
import org.allaymc.api.utils.identifier.Identifier;
import java.util.Set;
import java.util.stream.Collectors;
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -8,7 +8,7 @@ dependencies {
paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle)
shaded(project(":platforms:bukkit:common"))
shaded(project(":platforms:bukkit:nms:v1_21_9"))
shaded(project(":platforms:bukkit:nms"))
shaded("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
@@ -27,13 +27,13 @@ tasks {
}
runServer {
minecraftVersion(Versions.Bukkit.runPaperMinecraft)
minecraftVersion(Versions.Bukkit.minecraft)
dependsOn(shadowJar)
pluginJars(shadowJar.get().archiveFile)
downloadPlugins {
modrinth("viaversion", "5.3.2")
modrinth("viabackwards", "5.3.2")
modrinth("viaversion", "5.5.0")
modrinth("viabackwards", "5.5.0")
}
}
}
@@ -0,0 +1,55 @@
package com.dfsek.terra.bukkit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import com.dfsek.terra.bukkit.util.VersionUtil;
public interface NMSInitializer {
List<String> SUPPORTED_VERSIONS = List.of("v1.21.11");
String MINECRAFT_VERSION = VersionUtil.getMinecraftVersionInfo().toString();
String TERRA_PACKAGE = NMSInitializer.class.getPackageName();
static PlatformImpl init(TerraBukkitPlugin plugin) {
Logger logger = LoggerFactory.getLogger(NMSInitializer.class);
if(!SUPPORTED_VERSIONS.contains(MINECRAFT_VERSION)) {
logger.error("You are running your server on Minecraft version {} which is not supported by this version of Terra.",
MINECRAFT_VERSION);
String bypassKey = "IKnowThereAreNoNMSBindingsFor" + MINECRAFT_VERSION.replace(".", "_") + "ButIWillProceedAnyway";
if(System.getProperty(bypassKey) == null) {
logger.error("Because of this **TERRA HAS BEEN DISABLED**.");
logger.error("Do not come ask us why it is not working.");
logger.error("If you wish to proceed anyways, you can add the JVM System Property \"{}\" to enable the plugin.", bypassKey);
return null;
} else {
logger.error("");
logger.error("");
for(int i = 0; i < 20; i++) {
logger.error("PROCEEDING WITH AN EXISTING TERRA WORLD WILL RESULT IN CORRUPTION!!!");
}
logger.error("");
logger.error("");
logger.error("We will not give you any support for issues that may arise.");
logger.error("Since you enabled the \"{}\" flag, we won't disable Terra. But be warned.", bypassKey);
}
}
return constructPlatform(plugin);
}
private static PlatformImpl constructPlatform(TerraBukkitPlugin plugin) {
try {
Class<?> platformClass = Class.forName(TERRA_PACKAGE + ".nms.NMSPlatform");
return (PlatformImpl) platformClass
.getConstructor(TerraBukkitPlugin.class)
.newInstance(plugin);
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Error initializing NMS bindings. Report this to Terra.", e);
}
}
}
@@ -40,7 +40,6 @@ import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper;
import com.dfsek.terra.bukkit.listeners.CommonListener;
import com.dfsek.terra.bukkit.nms.Initializer;
import com.dfsek.terra.bukkit.util.PaperUtil;
import com.dfsek.terra.bukkit.util.VersionUtil;
import com.dfsek.terra.bukkit.world.BukkitAdapter;
@@ -60,7 +59,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
return;
}
platform = Initializer.init(this);
platform = NMSInitializer.init(this);
if(platform == null) {
Bukkit.getPluginManager().disablePlugin(this);
return;
@@ -94,7 +93,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
BukkitAdapter::adapt,
BukkitAdapter::adapt
))
.executionCoordinator(ExecutionCoordinator.simpleCoordinator())
.executionCoordinator(ExecutionCoordinator.asyncCoordinator())
.buildOnEnable(this);
commandManager.brigadierManager().setNativeNumberSuggestions(false);
@@ -1,60 +0,0 @@
package com.dfsek.terra.bukkit.nms;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.TerraBukkitPlugin;
import com.dfsek.terra.bukkit.util.VersionUtil;
public interface Initializer {
String NMS = VersionUtil.getMinecraftVersionInfo().toString().replace(".", "_");
String TERRA_PACKAGE = Initializer.class.getPackageName();
static PlatformImpl init(TerraBukkitPlugin plugin) {
Logger logger = LoggerFactory.getLogger(Initializer.class);
PlatformImpl platform = constructPlatform(plugin);
if(platform == null) {
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
String bypassKey = "IKnowThereAreNoNMSBindingsFor" + NMS + "ButIWillProceedAnyway";
if(System.getProperty(bypassKey) == null) {
logger.error("Because of this **TERRA HAS BEEN DISABLED**.");
logger.error("Do not come ask us why it is not working.");
logger.error("If you wish to proceed anyways, you can add the JVM System Property \"{}\" to enable the plugin.", bypassKey);
return null;
} else {
logger.error("");
logger.error("");
for(int i = 0; i < 20; i++) {
logger.error("PROCEEDING WITH AN EXISTING TERRA WORLD WILL RESULT IN CORRUPTION!!!");
}
logger.error("");
logger.error("");
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
logger.error("We will not give you any support for issues that may arise.");
logger.error("Since you enabled the \"{}\" flag, we won't disable Terra. But be warned.", bypassKey);
}
}
return platform;
}
private static PlatformImpl constructPlatform(TerraBukkitPlugin plugin) {
try {
Class<?> platformClass = Class.forName(TERRA_PACKAGE + "." + NMS + ".NMSPlatform");
try {
return (PlatformImpl) platformClass
.getConstructor(TerraBukkitPlugin.class)
.newInstance(plugin);
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Error initializing NMS bindings. Report this to Terra.", e);
}
} catch(ClassNotFoundException e) {
return null;
}
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
@@ -7,10 +7,10 @@ import net.minecraft.core.HolderSet.Named;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.entity.npc.villager.VillagerType;
import net.minecraft.world.level.biome.Biome;
import org.bukkit.NamespacedKey;
import org.slf4j.Logger;
@@ -25,7 +25,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.VanillaBiomeProperties;
import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties;
import com.dfsek.terra.bukkit.world.BukkitBiomeInfo;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
import com.dfsek.terra.registry.master.ConfigRegistry;
@@ -34,7 +34,7 @@ import com.dfsek.terra.registry.master.ConfigRegistry;
public class AwfulBukkitHacks {
private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.class);
private static final Map<ResourceLocation, List<ResourceLocation>> terraBiomeMap = new HashMap<>();
private static final Map<Identifier, List<Identifier>> terraBiomeMap = new HashMap<>();
public static void registerBiomes(ConfigRegistry configRegistry) {
try {
@@ -50,7 +50,7 @@ public class AwfulBukkitHacks {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey();
ResourceLocation vanillaMinecraftKey = ResourceLocation.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(),
Identifier vanillaMinecraftKey = Identifier.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(),
vanillaBukkitKey.getKey());
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
@@ -58,7 +58,7 @@ public class AwfulBukkitHacks {
Biome platform = NMSBiomeInjector.createBiome(biomeRegistry.get(vanillaMinecraftKey).orElseThrow().value(),
vanillaBiomeProperties);
ResourceLocation delegateMinecraftKey = ResourceLocation.fromNamespaceAndPath("terra",
Identifier delegateMinecraftKey = Identifier.fromNamespaceAndPath("terra",
NMSBiomeInjector.createBiomeID(pack, key));
NamespacedKey delegateBukkitKey = NamespacedKey.fromString(delegateMinecraftKey.toString());
ResourceKey<Biome> delegateKey = ResourceKey.create(Registries.BIOME, delegateMinecraftKey);
@@ -75,7 +75,7 @@ public class AwfulBukkitHacks {
Objects.requireNonNullElse(vanillaBiomeProperties.getVillagerType(),
villagerMap.getOrDefault(delegateKey, VillagerType.PLAINS)));
terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.location());
terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.identifier());
LOGGER.debug("Registered biome: " + delegateKey);
} catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
@@ -100,8 +100,8 @@ public class AwfulBukkitHacks {
tb -> NMSBiomeInjector.getEntry(biomeRegistry, tb).ifPresentOrElse(
terra -> {
LOGGER.debug("{} (vanilla for {}): {}",
vanilla.unwrapKey().orElseThrow().location(),
terra.unwrapKey().orElseThrow().location(),
vanilla.unwrapKey().orElseThrow().identifier(),
terra.unwrapKey().orElseThrow().identifier(),
vanilla.tags().toList());
vanilla.tags()
.forEach(tag -> collect
@@ -1,11 +1,11 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.bukkit.BukkitAddon;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.VanillaBiomeProperties;
import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties;
public class NMSAddon extends BukkitAddon {
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
@@ -0,0 +1,108 @@
package com.dfsek.terra.bukkit.nms;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.Identifier;
import net.minecraft.world.attribute.EnvironmentAttributes;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, Identifier identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.flatMap(registry::get);
}
public static Biome createBiome(Biome vanilla, VanillaBiomeProperties vanillaBiomeProperties)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder();
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
// TODO: Migrate to EnvironmentAttributes
// builder.setAttribute(EnvironmentAttributes.FOG_COLOR, Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getAttributes().get(EnvironmentAttributes.FOG_COLOR)))
// effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()));
effects.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
// .waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
// .skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()))
.grassColorModifier(Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColorModifier(),
vanilla.getSpecialEffects().grassColorModifier()));
// .backgroundMusicVolume(Objects.requireNonNullElse(vanillaBiomeProperties.getMusicVolume(), vanilla.getBackgroundMusicVolume()));
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().grassColorOverride().ifPresent(effects::grassColorOverride);
} else {
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().foliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
// TODO
// if(vanillaBiomeProperties.getParticleConfig() == null) {
// vanilla.getSpecialEffects().getAmbientParticleSettings().ifPresent(effects::ambientParticle);
// } else {
// effects.ambientParticle(vanillaBiomeProperties.getParticleConfig());
// }
// if(vanillaBiomeProperties.getLoopSound() == null) {
// vanilla.getSpecialEffects().getAmbientLoopSoundEvent().ifPresent(effects::ambientLoopSound);
// } else {
// RegistryFetcher.soundEventRegistry().get(vanillaBiomeProperties.getLoopSound().location()).ifPresent(effects::ambientLoopSound);
// }
// if(vanillaBiomeProperties.getMoodSound() == null) {
// vanilla.getSpecialEffects().getAmbientMoodSettings().ifPresent(effects::ambientMoodSound);
// } else {
// effects.ambientMoodSound(vanillaBiomeProperties.getMoodSound());
// }
//
// if(vanillaBiomeProperties.getAdditionsSound() == null) {
// vanilla.getSpecialEffects().getAmbientAdditionsSettings().ifPresent(effects::ambientAdditionsSound);
// } else {
// effects.ambientAdditionsSound(vanillaBiomeProperties.getAdditionsSound());
// }
//
// if(vanillaBiomeProperties.getMusic() == null) {
// vanilla.getSpecialEffects().getBackgroundMusic().ifPresent(effects::backgroundMusic);
// } else {
// effects.backgroundMusic(vanillaBiomeProperties.getMusic());
// }
builder.hasPrecipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.hasPrecipitation()));
builder.temperature(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperature(), vanilla.getBaseTemperature()));
builder.downfall(Objects.requireNonNullElse(vanillaBiomeProperties.getDownfall(), vanilla.climateSettings.downfall()));
builder.temperatureAdjustment(
Objects.requireNonNullElse(vanillaBiomeProperties.getTemperatureModifier(), vanilla.climateSettings.temperatureModifier()));
builder.mobSpawnSettings(Objects.requireNonNullElse(vanillaBiomeProperties.getSpawnSettings(), vanilla.getMobSettings()));
return builder
.specialEffects(effects.build())
.generationSettings(new BiomeGenerationSettings.PlainBuilder().build())
.build();
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.Holder;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
@@ -1,16 +1,16 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.exception.LoadException;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.attribute.AmbientParticle;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.level.biome.AmbientAdditionsSettings;
import net.minecraft.world.level.biome.AmbientMoodSettings;
import net.minecraft.world.level.biome.AmbientParticleSettings;
import net.minecraft.world.attribute.AmbientAdditionsSettings;
import net.minecraft.world.attribute.AmbientMoodSettings;
import net.minecraft.world.entity.npc.villager.VillagerType;
import net.minecraft.world.level.biome.Biome.Precipitation;
import net.minecraft.world.level.biome.Biome.TemperatureModifier;
import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier;
@@ -27,17 +27,17 @@ import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.world.biome.PlatformBiome;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.TerraBukkitPlugin;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.BiomeAdditionsSoundTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.BiomeMoodSoundTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.BiomeParticleConfigTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.EntityTypeTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.MusicSoundTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.SoundEventTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.SpawnCostConfig;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.SpawnEntryConfig;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.SpawnSettingsTemplate;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.SpawnTypeConfig;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.VillagerTypeTemplate;
import com.dfsek.terra.bukkit.nms.config.BiomeAdditionsSoundTemplate;
import com.dfsek.terra.bukkit.nms.config.BiomeMoodSoundTemplate;
import com.dfsek.terra.bukkit.nms.config.BiomeParticleConfigTemplate;
import com.dfsek.terra.bukkit.nms.config.EntityTypeTemplate;
import com.dfsek.terra.bukkit.nms.config.MusicSoundTemplate;
import com.dfsek.terra.bukkit.nms.config.SoundEventTemplate;
import com.dfsek.terra.bukkit.nms.config.SpawnCostConfig;
import com.dfsek.terra.bukkit.nms.config.SpawnEntryConfig;
import com.dfsek.terra.bukkit.nms.config.SpawnSettingsTemplate;
import com.dfsek.terra.bukkit.nms.config.SpawnTypeConfig;
import com.dfsek.terra.bukkit.nms.config.VillagerTypeTemplate;
public class NMSPlatform extends PlatformImpl {
@@ -52,8 +52,8 @@ public class NMSPlatform extends PlatformImpl {
public void register(TypeRegistry registry) {
super.register(registry);
registry.registerLoader(PlatformBiome.class, (type, o, loader, depthTracker) -> parseBiome((String) o, depthTracker))
.registerLoader(ResourceLocation.class, (type, o, loader, depthTracker) -> {
ResourceLocation identifier = ResourceLocation.tryParse((String) o);
.registerLoader(Identifier.class, (type, o, loader, depthTracker) -> {
Identifier identifier = Identifier.tryParse((String) o);
if(identifier == null)
throw new LoadException("Invalid identifier: " + o, depthTracker);
return identifier;
@@ -67,7 +67,7 @@ public class NMSPlatform extends PlatformImpl {
(type, o, loader, depthTracker) -> TemperatureModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(MobCategory.class, (type, o, loader, depthTracker) -> MobCategory.valueOf((String) o))
.registerLoader(AmbientParticleSettings.class, BiomeParticleConfigTemplate::new)
.registerLoader(AmbientParticle.class, BiomeParticleConfigTemplate::new)
.registerLoader(SoundEvent.class, SoundEventTemplate::new)
.registerLoader(AmbientMoodSettings.class, BiomeMoodSoundTemplate::new)
.registerLoader(AmbientAdditionsSettings.class, BiomeAdditionsSoundTemplate::new)
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.world.level.LevelHeightAccessor;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
@@ -7,7 +7,7 @@ import net.minecraft.core.MappedRegistry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.entity.npc.villager.VillagerType;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.Biome;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
package com.dfsek.terra.bukkit.nms;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
@@ -1,11 +1,11 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.biome.AmbientAdditionsSettings;
import net.minecraft.world.attribute.AmbientAdditionsSettings;
public class BiomeAdditionsSoundTemplate implements ObjectTemplate<AmbientAdditionsSettings> {
@@ -1,11 +1,11 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.biome.AmbientMoodSettings;
import net.minecraft.world.attribute.AmbientMoodSettings;
public class BiomeMoodSoundTemplate implements ObjectTemplate<AmbientMoodSettings> {
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -8,12 +8,12 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.arguments.ParticleArgument;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.biome.AmbientParticleSettings;
import net.minecraft.world.attribute.AmbientParticle;
import java.util.stream.Stream;
public class BiomeParticleConfigTemplate implements ObjectTemplate<AmbientParticleSettings> {
public class BiomeParticleConfigTemplate implements ObjectTemplate<AmbientParticle> {
@Value("particle")
@Default
private String particle = null;
@@ -23,13 +23,13 @@ public class BiomeParticleConfigTemplate implements ObjectTemplate<AmbientPartic
private Float probability = 0.1f;
@Override
public AmbientParticleSettings get() {
public AmbientParticle get() {
if(particle == null) {
return null;
}
try {
return new AmbientParticleSettings(ParticleArgument.readParticle(new StringReader(particle),
return new AmbientParticle(ParticleArgument.readParticle(new StringReader(particle),
HolderLookup.Provider.create(Stream.of(BuiltInRegistries.PARTICLE_TYPE))), probability);
} catch(CommandSyntaxException e) {
throw new RuntimeException(e);
@@ -1,17 +1,17 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.EntityType;
public class EntityTypeTemplate implements ObjectTemplate<EntityType<?>> {
@Value("id")
@Default
private ResourceLocation id = null;
private Identifier id = null;
@Override
public EntityType<?> get() {
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -1,16 +1,16 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundEvent;
public class SoundEventTemplate implements ObjectTemplate<SoundEvent> {
@Value("id")
@Default
private ResourceLocation id = null;
private Identifier id = null;
@Value("distance-to-travel")
@Default
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -6,6 +6,8 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
import com.dfsek.terra.api.util.range.Range;
public class SpawnEntryConfig implements ObjectTemplate<SpawnEntryConfig> {
@Value("type")
@@ -16,20 +18,16 @@ public class SpawnEntryConfig implements ObjectTemplate<SpawnEntryConfig> {
@Default
private Integer weight = null;
@Value("min-group-size")
@Value("group-size")
@Default
private Integer minGroupSize = null;
@Value("max-group-size")
@Default
private Integer maxGroupSize = null;
private Range groupSize = null;
public Integer getWeight() {
return weight;
}
public SpawnerData getSpawnEntry() {
return new SpawnerData(type, minGroupSize, maxGroupSize);
return new SpawnerData(type, groupSize.getMin(), groupSize.getMax());
}
@Override
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
@@ -6,10 +6,10 @@ import com.dfsek.tectonic.api.config.template.annotations.Value;
import net.minecraft.resources.ResourceKey;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.level.biome.AmbientAdditionsSettings;
import net.minecraft.world.level.biome.AmbientMoodSettings;
import net.minecraft.world.level.biome.AmbientParticleSettings;
import net.minecraft.world.entity.npc.villager.VillagerType;
import net.minecraft.world.attribute.AmbientAdditionsSettings;
import net.minecraft.world.attribute.AmbientMoodSettings;
import net.minecraft.world.attribute.AmbientParticle;
import net.minecraft.world.level.biome.Biome.TemperatureModifier;
import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier;
import net.minecraft.world.level.biome.MobSpawnSettings;
@@ -52,7 +52,7 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("particles")
@Default
private AmbientParticleSettings particleConfig = null;
private AmbientParticle particleConfig = null;
@Value("climate.precipitation")
@Default
@@ -130,7 +130,7 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
return grassColorModifier;
}
public AmbientParticleSettings getParticleConfig() {
public AmbientParticle getParticleConfig() {
return particleConfig;
}
@@ -1,18 +1,18 @@
package com.dfsek.terra.bukkit.nms.v1_21_9.config;
package com.dfsek.terra.bukkit.nms.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.npc.villager.VillagerType;
public class VillagerTypeTemplate implements ObjectTemplate<ResourceKey<VillagerType>> {
@Value("id")
@Default
private ResourceLocation id = null;
private Identifier id = null;
@Override
public ResourceKey<VillagerType> get() {
@@ -1,103 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_21_9;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.nms.v1_21_9.config.VanillaBiomeProperties;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, ResourceLocation identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.flatMap(registry::get);
}
public static Biome createBiome(Biome vanilla, VanillaBiomeProperties vanillaBiomeProperties)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder();
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()))
.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
.waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
.skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()))
.grassColorModifier(Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColorModifier(),
vanilla.getSpecialEffects().getGrassColorModifier()))
.backgroundMusicVolume(Objects.requireNonNullElse(vanillaBiomeProperties.getMusicVolume(), vanilla.getBackgroundMusicVolume()));
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride);
} else {
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
if(vanillaBiomeProperties.getParticleConfig() == null) {
vanilla.getSpecialEffects().getAmbientParticleSettings().ifPresent(effects::ambientParticle);
} else {
effects.ambientParticle(vanillaBiomeProperties.getParticleConfig());
}
if(vanillaBiomeProperties.getLoopSound() == null) {
vanilla.getSpecialEffects().getAmbientLoopSoundEvent().ifPresent(effects::ambientLoopSound);
} else {
RegistryFetcher.soundEventRegistry().get(vanillaBiomeProperties.getLoopSound().location()).ifPresent(effects::ambientLoopSound);
}
if(vanillaBiomeProperties.getMoodSound() == null) {
vanilla.getSpecialEffects().getAmbientMoodSettings().ifPresent(effects::ambientMoodSound);
} else {
effects.ambientMoodSound(vanillaBiomeProperties.getMoodSound());
}
if(vanillaBiomeProperties.getAdditionsSound() == null) {
vanilla.getSpecialEffects().getAmbientAdditionsSettings().ifPresent(effects::ambientAdditionsSound);
} else {
effects.ambientAdditionsSound(vanillaBiomeProperties.getAdditionsSound());
}
if(vanillaBiomeProperties.getMusic() == null) {
vanilla.getSpecialEffects().getBackgroundMusic().ifPresent(effects::backgroundMusic);
} else {
effects.backgroundMusic(vanillaBiomeProperties.getMusic());
}
builder.hasPrecipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.hasPrecipitation()));
builder.temperature(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperature(), vanilla.getBaseTemperature()));
builder.downfall(Objects.requireNonNullElse(vanillaBiomeProperties.getDownfall(), vanilla.climateSettings.downfall()));
builder.temperatureAdjustment(
Objects.requireNonNullElse(vanillaBiomeProperties.getTemperatureModifier(), vanilla.climateSettings.temperatureModifier()));
builder.mobSpawnSettings(Objects.requireNonNullElse(vanillaBiomeProperties.getSpawnSettings(), vanilla.getMobSettings()));
return builder
.specialEffects(effects.build())
.generationSettings(new BiomeGenerationSettings.PlainBuilder().build())
.build();
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import java.util.*
plugins {
id("dev.architectury.loom") version Versions.Mod.architecuryLoom
id("dev.architectury.loom") version Versions.Mod.architecturyLoom
id("architectury-plugin") version Versions.Mod.architecturyPlugin
}
@@ -35,7 +35,7 @@
"depends": {
"fabricloader": ">=0.16.10",
"java": ">=21",
"minecraft": "1.21.9",
"minecraft": "1.21.10",
"fabric": "*"
}
}
+5 -1
View File
@@ -3,9 +3,13 @@ dependencies {
shadedApi("com.github.ben-manes.caffeine", "caffeine", Versions.Libraries.caffeine)
shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
compileOnly("net.minestom", "minestom-snapshots", Versions.Minestom.minestom)
compileOnly("net.minestom", "minestom", Versions.Minestom.minestom)
}
tasks.named("jar") {
finalizedBy("installAddonsIntoDefaultJar")
}
tasks.withType<JavaCompile>().configureEach {
options.release = 25
}
+5 -1
View File
@@ -7,7 +7,7 @@ val javaMainClass = "com.dfsek.terra.minestom.TerraMinestomExample"
dependencies {
shadedApi(project(":platforms:minestom"))
implementation("net.minestom", "minestom-snapshots", Versions.Minestom.minestom)
implementation("net.minestom", "minestom", Versions.Minestom.minestom)
implementation("org.slf4j", "slf4j-simple", Versions.Libraries.slf4j)
}
@@ -20,6 +20,10 @@ tasks.withType<Jar> {
}
}
tasks.withType<JavaCompile>().configureEach {
options.release = 25
}
application {
mainClass.set(javaMainClass)
}
@@ -9,6 +9,7 @@ import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.LightingChunk;
import net.minestom.server.world.DimensionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,6 +27,8 @@ public class TerraMinestomExample {
private TerraMinestomWorld world;
public static void main(String[] args) {
System.setProperty("minestom.registry.unsafe-ops", "true");
TerraMinestomExample example = new TerraMinestomExample();
example.createNewInstance();
example.attachTerra();
@@ -43,7 +46,7 @@ public class TerraMinestomExample {
public void attachTerra() {
world = platform.worldBuilder(instance)
.defaultPack()
.packByDefaultMeta(DimensionType.OVERWORLD)
.attach();
}
@@ -7,8 +7,11 @@ import net.kyori.adventure.util.RGBLike;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Instance;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.world.attribute.AmbientParticle;
import net.minestom.server.world.attribute.AmbientSounds;
import net.minestom.server.world.biome.BiomeEffects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,8 +28,11 @@ import com.dfsek.terra.api.handle.ItemHandle;
import com.dfsek.terra.api.handle.WorldHandle;
import com.dfsek.terra.api.world.biome.PlatformBiome;
import com.dfsek.terra.minestom.addon.MinestomAddon;
import com.dfsek.terra.minestom.api.BiomeFactory;
import com.dfsek.terra.minestom.api.TerraMinestomWorldBuilder;
import com.dfsek.terra.minestom.biome.MinestomBiomeLoader;
import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomeFactory;
import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomePool;
import com.dfsek.terra.minestom.config.BiomeAdditionsSoundTemplate;
import com.dfsek.terra.minestom.config.BiomeMoodSoundTemplate;
import com.dfsek.terra.minestom.config.BiomeParticleConfigTemplate;
@@ -45,19 +51,26 @@ public final class TerraMinestomPlatform extends AbstractPlatform {
private final ItemHandle itemHandle;
private final TypeLoader<PlatformBiome> biomeTypeLoader;
private final ArrayList<BaseAddon> platformAddons = new ArrayList<>(List.of(new MinestomAddon(this)));
private final MinestomUserDefinedBiomePool biomePool;
public TerraMinestomPlatform(WorldHandle worldHandle, ItemHandle itemHandle, TypeLoader<PlatformBiome> biomeTypeLoader,
BaseAddon... extraAddons) {
BiomeFactory biomeFactory, BaseAddon... extraAddons) {
this.worldHandle = worldHandle;
this.itemHandle = itemHandle;
this.biomeTypeLoader = biomeTypeLoader;
this.biomePool = new MinestomUserDefinedBiomePool(biomeFactory);
this.platformAddons.addAll(List.of(extraAddons));
load();
getEventManager().callEvent(new PlatformInitializationEvent());
initializeRegistry(); // Needs to be called before minecraft server bind
}
public TerraMinestomPlatform() {
this(new MinestomWorldHandle(), new MinestomItemHandle(), new MinestomBiomeLoader());
this(new MinestomWorldHandle(), new MinestomItemHandle(), new MinestomBiomeLoader(), new MinestomUserDefinedBiomeFactory());
}
public static Builder builder() {
return new Builder();
}
@Override
@@ -71,9 +84,9 @@ public final class TerraMinestomPlatform extends AbstractPlatform {
(TypeLoader<EntityType>) (annotatedType, o, configLoader, depthTracker) -> new MinestomEntityType((String) o))
.registerLoader(BlockState.class,
(TypeLoader<BlockState>) (annotatedType, o, configLoader, depthTracker) -> worldHandle.createBlockState((String) o))
.registerLoader(BiomeEffects.Particle.class, BiomeParticleConfigTemplate::new)
.registerLoader(BiomeEffects.MoodSound.class, BiomeMoodSoundTemplate::new)
.registerLoader(BiomeEffects.AdditionsSound.class, BiomeAdditionsSoundTemplate::new)
.registerLoader(AmbientParticle.class, BiomeParticleConfigTemplate::new)
.registerLoader(AmbientSounds.Mood.class, BiomeMoodSoundTemplate::new)
.registerLoader(AmbientSounds.Additions.class, BiomeAdditionsSoundTemplate::new)
.registerLoader(SoundEvent.class, SoundEventTemplate::new);
}
@@ -94,6 +107,10 @@ public final class TerraMinestomPlatform extends AbstractPlatform {
return succeed;
}
public void initializeRegistry() {
getRawConfigRegistry()
.forEach(pack -> biomePool.preloadBiomes(pack, pack.getBiomeProvider().getBiomes()));
}
@Override
public @NotNull WorldHandle getWorldHandle() {
@@ -125,10 +142,59 @@ public final class TerraMinestomPlatform extends AbstractPlatform {
}
public TerraMinestomWorldBuilder worldBuilder(Instance instance) {
return new TerraMinestomWorldBuilder(this, instance);
return new TerraMinestomWorldBuilder(this, instance, biomePool);
}
public TerraMinestomWorldBuilder worldBuilder() {
return new TerraMinestomWorldBuilder(this, MinecraftServer.getInstanceManager().createInstanceContainer());
return worldBuilder(MinecraftServer.getInstanceManager().createInstanceContainer());
}
public static class Builder {
private final List<BaseAddon> platformAddons = new ArrayList<>();
private @Nullable WorldHandle worldHandle;
private @Nullable ItemHandle itemHandle;
private @Nullable TypeLoader<PlatformBiome> biomeTypeLoader;
private @Nullable BiomeFactory biomeFactory;
public Builder worldHandle(@Nullable WorldHandle worldHandle) {
this.worldHandle = worldHandle;
return this;
}
public Builder itemHandle(@Nullable ItemHandle itemHandle) {
this.itemHandle = itemHandle;
return this;
}
public Builder biomeTypeLoader(@Nullable TypeLoader<PlatformBiome> biomeTypeLoader) {
this.biomeTypeLoader = biomeTypeLoader;
return this;
}
public Builder addPlatformAddon(BaseAddon addon) {
this.platformAddons.add(addon);
return this;
}
public Builder biomeFactory(BiomeFactory biomeFactory) {
this.biomeFactory = biomeFactory;
return this;
}
public TerraMinestomPlatform build() {
if(worldHandle == null) worldHandle = new MinestomWorldHandle();
if(itemHandle == null) itemHandle = new MinestomItemHandle();
if(biomeTypeLoader == null) biomeTypeLoader = new MinestomBiomeLoader();
if(biomeFactory == null) biomeFactory = new MinestomUserDefinedBiomeFactory();
return new TerraMinestomPlatform(
worldHandle,
itemHandle,
biomeTypeLoader,
biomeFactory,
platformAddons.toArray(new BaseAddon[0])
);
}
}
}
@@ -2,6 +2,8 @@ package com.dfsek.terra.minestom.addon;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.version.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
@@ -10,9 +12,6 @@ import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.minestom.TerraMinestomPlatform;
import com.dfsek.terra.minestom.config.VanillaBiomeProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MinestomAddon implements BaseAddon {
private static final Version VERSION = Versions.getVersion(1, 0, 0);
@@ -1,6 +1,7 @@
package com.dfsek.terra.minestom.api;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
@@ -9,5 +10,22 @@ import net.minestom.server.entity.EntityType;
* Allows adding AI to generated entities using custom entity types
*/
public interface EntityFactory {
/**
* Creates a new entity of the specified type.
*
* @param type the type of the entity to be created
* @return the created entity instance
*/
Entity createEntity(EntityType type);
/**
* Creates a new entity of the specified type with additional data.
*
* @param type the type of the entity to be created
* @param data the additional data for the entity, represented as a CompoundBinaryTag
* @return the created entity instance
*/
default Entity createEntity(EntityType type, CompoundBinaryTag data) {
return createEntity(type);
}
}
@@ -1,6 +1,9 @@
package com.dfsek.terra.minestom.api;
import net.minestom.server.instance.Instance;
import net.minestom.server.registry.RegistryKey;
import net.minestom.server.world.DimensionType;
import org.jspecify.annotations.NonNull;
import java.util.Random;
import java.util.function.Function;
@@ -8,7 +11,7 @@ import java.util.function.Function;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.minestom.TerraMinestomPlatform;
import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomeFactory;
import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomePool;
import com.dfsek.terra.minestom.block.DefaultBlockEntityFactory;
import com.dfsek.terra.minestom.entity.DefaultEntityFactory;
import com.dfsek.terra.minestom.world.TerraMinestomWorld;
@@ -17,16 +20,17 @@ import com.dfsek.terra.minestom.world.TerraMinestomWorld;
public class TerraMinestomWorldBuilder {
private final TerraMinestomPlatform platform;
private final Instance instance;
private final MinestomUserDefinedBiomePool biomePool;
private ConfigPack pack;
private long seed = new Random().nextLong();
private EntityFactory entityFactory = new DefaultEntityFactory();
private BlockEntityFactory blockEntityFactory;
private BiomeFactory biomeFactory = new MinestomUserDefinedBiomeFactory();
public TerraMinestomWorldBuilder(TerraMinestomPlatform platform, Instance instance) {
public TerraMinestomWorldBuilder(TerraMinestomPlatform platform, Instance instance, MinestomUserDefinedBiomePool biomePool) {
this.platform = platform;
this.instance = instance;
this.blockEntityFactory = new DefaultBlockEntityFactory(instance);
this.biomePool = biomePool;
}
public TerraMinestomWorldBuilder pack(ConfigPack pack) {
@@ -36,10 +40,22 @@ public class TerraMinestomWorldBuilder {
public TerraMinestomWorldBuilder packById(String id) {
this.pack = platform.getConfigRegistry().getByID(id).orElseThrow();
return this;
}
public TerraMinestomWorldBuilder packByMeta(String metaPack, RegistryKey<@NonNull DimensionType> dimensionType) {
this.pack = platform.getMetaConfigRegistry()
.getByID(metaPack)
.orElseThrow(() -> new RuntimeException("MetaPack " + metaPack + " could not be found"))
.packs()
.get(dimensionType.key().asString());
return this;
}
public TerraMinestomWorldBuilder packByDefaultMeta(RegistryKey<@NonNull DimensionType> dimensionType) {
return packByMeta("DEFAULT", dimensionType);
}
public TerraMinestomWorldBuilder findPack(Function<CheckedRegistry<ConfigPack>, ConfigPack> fn) {
this.pack = fn.apply(platform.getConfigRegistry());
return this;
@@ -64,12 +80,7 @@ public class TerraMinestomWorldBuilder {
return this;
}
public TerraMinestomWorldBuilder biomeFactory(BiomeFactory factory) {
this.biomeFactory = factory;
return this;
}
public TerraMinestomWorld attach() {
return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomeFactory);
return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomePool);
}
}
@@ -5,6 +5,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.color.Color;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.RegistryKey;
import net.minestom.server.world.attribute.EnvironmentAttribute;
import net.minestom.server.world.biome.Biome;
import net.minestom.server.world.biome.BiomeEffects;
import org.intellij.lang.annotations.Subst;
@@ -27,6 +28,11 @@ public class MinestomUserDefinedBiomeFactory implements BiomeFactory {
return first;
}
private static <T> void applyAttributeIfNotNull(Biome.Builder biomeBuilder, EnvironmentAttribute<T> attribute, T value) {
if (value == null) return;
biomeBuilder.setAttribute(attribute, value);
}
@Subst("value")
protected static String createBiomeID(ConfigPack pack, String biomeId) {
return pack.getID().toLowerCase() + "/" + biomeId.toLowerCase(Locale.ROOT);
@@ -41,32 +47,25 @@ public class MinestomUserDefinedBiomeFactory implements BiomeFactory {
Key key = Key.key("terra", createBiomeID(pack, source.getID()));
BiomeEffects.Builder effectsBuilder = BiomeEffects.builder()
.fogColor(mergeNullable(properties.getFogColor(), parentEffects.fogColor()))
.skyColor(mergeNullable(properties.getSkyColor(), parentEffects.skyColor()))
.waterColor(mergeNullable(properties.getWaterColor(), parentEffects.waterColor()))
.waterFogColor(mergeNullable(properties.getWaterFogColor(), parentEffects.waterFogColor()))
.foliageColor(mergeNullable(properties.getFoliageColor(), parentEffects.foliageColor()))
.grassColor(mergeNullable(properties.getGrassColor(), parentEffects.grassColor()))
.grassColorModifier(mergeNullable(properties.getGrassColorModifier(), parentEffects.grassColorModifier()))
.biomeParticle(mergeNullable(properties.getParticleConfig(), parentEffects.biomeParticle()))
.ambientSound(mergeNullable(properties.getLoopSound(), parentEffects.ambientSound()))
.moodSound(mergeNullable(properties.getMoodSound(), parentEffects.moodSound()))
.additionsSound(mergeNullable(properties.getAdditionsSound(), parentEffects.additionsSound()))
// TODO music
.music(parentEffects.music())
.musicVolume(parentEffects.musicVolume());
.grassColorModifier(mergeNullable(properties.getGrassColorModifier(), parentEffects.grassColorModifier()));
if(effectsBuilder.build().equals(BiomeEffects.PLAINS_EFFECTS)) {
effectsBuilder.fogColor(new Color(0xC0D8FE)); // circumvent a minestom bug
}
Biome target = Biome.builder()
Biome.Builder targetBuilder = Biome.builder()
.downfall(mergeNullable(properties.getDownfall(), parent.downfall()))
.hasPrecipitation(mergeNullable(properties.getPrecipitation(), parent.hasPrecipitation()))
.precipitation(mergeNullable(properties.getPrecipitation(), parent.hasPrecipitation()))
.temperature(mergeNullable(properties.getTemperature(), parent.temperature()))
.temperatureModifier(mergeNullable(properties.getTemperatureModifier(), parent.temperatureModifier()))
.effects(effectsBuilder.build())
.build();
.effects(effectsBuilder.build());
applyAttributeIfNotNull(targetBuilder, EnvironmentAttribute.FOG_COLOR, properties.getFogColor());
applyAttributeIfNotNull(targetBuilder, EnvironmentAttribute.SKY_COLOR, properties.getSkyColor());
applyAttributeIfNotNull(targetBuilder, EnvironmentAttribute.WATER_FOG_COLOR, properties.getWaterFogColor());
applyAttributeIfNotNull(targetBuilder, EnvironmentAttribute.AMBIENT_PARTICLES, properties.getParticleConfig());
applyAttributeIfNotNull(targetBuilder, EnvironmentAttribute.AMBIENT_SOUNDS, properties.getAmbientSoundConfig());
// TODO music
Biome target = targetBuilder.build();
RegistryKey<Biome> registryKey = MinecraftServer.getBiomeRegistry().register(key, target);
return new UserDefinedBiome(key, registryKey, source.getID(), target);
@@ -12,14 +12,12 @@ public class MinestomUserDefinedBiomePool {
private final IdentityHashMap<Biome, UserDefinedBiome> biomes = new IdentityHashMap<>();
private final HashSet<String> createdBiomes = new HashSet<>();
private final BiomeFactory factory;
private final ConfigPack configPack;
public MinestomUserDefinedBiomePool(ConfigPack configPack, BiomeFactory factory) {
this.configPack = configPack;
public MinestomUserDefinedBiomePool(BiomeFactory factory) {
this.factory = factory;
}
public UserDefinedBiome getBiome(Biome source) {
public UserDefinedBiome getBiome(ConfigPack configPack, Biome source) {
UserDefinedBiome userDefinedBiome = biomes.get(source);
if(userDefinedBiome != null) return userDefinedBiome;
userDefinedBiome = factory.create(configPack, source);
@@ -28,7 +26,7 @@ public class MinestomUserDefinedBiomePool {
return userDefinedBiome;
}
public void preloadBiomes(Iterable<Biome> biomesToLoad) {
public void preloadBiomes(ConfigPack configPack, Iterable<Biome> biomesToLoad) {
biomesToLoad
.forEach(biome -> {
if(!this.createdBiomes.contains(biome.getID())) {
@@ -1,7 +1,10 @@
package com.dfsek.terra.minestom.block;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.minestom.server.instance.block.Block;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -10,37 +13,84 @@ import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.properties.Property;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MinestomBlockState implements BlockState {
private final Block block;
public MinestomBlockState(Block block) {
if(block == null) {
this.block = Block.AIR;
} else {
this.block = block;
}
public record MinestomBlockState(Block block) implements BlockState {
private static final Logger LOGGER = LoggerFactory.getLogger(MinestomBlockState.class);
public static final MinestomBlockState AIR = new MinestomBlockState(Block.AIR);
private static final TagStringIO tagStringIO = TagStringIO.tagStringIO();
public MinestomBlockState {
block = Objects.requireNonNullElse(block, Block.AIR);
}
public MinestomBlockState(String data) {
if(!data.contains("[")) {
block = Block.fromKey(data);
return;
public static MinestomBlockState fromStateId(String data) {
CompoundBinaryTag nbt = CompoundBinaryTag.empty();
int splitIndex = data.indexOf('{');
if(splitIndex != -1) {
String fullId = data;
data = data.substring(0, splitIndex);
String dataString = fullId.substring(splitIndex);
try {
nbt = tagStringIO.asCompound(dataString);
} catch(IOException exception) {
LOGGER.warn("Invalid entity data, will be ignored: {}", dataString);
}
}
String[] split = data.split("\\[");
String namespaceId = split[0];
String properties = split[1].substring(0, split[1].length() - 1);
int openBracketIndex = data.indexOf('[');
int closeBracketIndex = data.indexOf(']');
if(openBracketIndex == -1 || closeBracketIndex == -1 || closeBracketIndex < openBracketIndex) {
// no or invalid properties
Block block = Block.fromKey(data);
if(block != null && !nbt.isEmpty()) {
block = block.withNbt(nbt);
}
return new MinestomBlockState(block);
}
String namespaceId = data.substring(0, openBracketIndex);
String propertiesContent = data.substring(openBracketIndex + 1, closeBracketIndex);
Block block = Block.fromKey(namespaceId);
HashMap<String, String> propertiesMap = new HashMap<>();
for(String property : properties.split(",")) {
String[] kv = property.split("=");
propertiesMap.put(kv[0].strip(), kv[1].strip());
if (block == null) {
LOGGER.error("Invalid block ID found during parsing: {}", namespaceId);
return new MinestomBlockState(Block.AIR);
}
assert block != null;
this.block = block.withProperties(propertiesMap);
HashMap<String, String> propertiesMap = new HashMap<>();
int current = 0;
while (current < propertiesContent.length()) {
int nextComma = propertiesContent.indexOf(',', current);
String property;
if (nextComma == -1) {
property = propertiesContent.substring(current);
current = propertiesContent.length();
} else {
property = propertiesContent.substring(current, nextComma);
current = nextComma + 1;
}
int equalsIndex = property.indexOf('=');
if (equalsIndex == -1) {
LOGGER.warn("Invalid block property syntax (missing '=') in string: {}", property);
continue;
}
String key = property.substring(0, equalsIndex).strip();
String value = property.substring(equalsIndex + 1).strip();
propertiesMap.put(key, value);
}
if(!nbt.isEmpty()) {
block = block.withNbt(nbt);
}
return new MinestomBlockState(block.withProperties(propertiesMap));
}
@Override
@@ -15,24 +15,27 @@ import com.dfsek.terra.minestom.block.MinestomBlockState;
public class CachedChunk implements ProtoChunk {
private final int minHeight;
private final int maxHeight;
private final Block[] blocks;
private final MinestomBlockState[] blocks;
public CachedChunk(int minHeight, int maxHeight) {
this.minHeight = minHeight;
this.maxHeight = maxHeight;
this.blocks = new Block[16 * (maxHeight - minHeight + 1) * 16];
Arrays.fill(blocks, Block.AIR);
this.blocks = new MinestomBlockState[16 * (maxHeight - minHeight + 1) * 16];
Arrays.fill(blocks, MinestomBlockState.AIR);
}
public void writeRelative(UnitModifier modifier) {
modifier.setAllRelative((x, y, z) -> blocks[getIndex(x, y + minHeight, z)]);
modifier.setAllRelative((x, y, z) -> blocks[getIndex(x, y + minHeight, z)].block());
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockState blockState) {
Block block = (Block) blockState.getHandle();
MinestomBlockState minestomBlockState = (MinestomBlockState) blockState;
Block block = minestomBlockState.block();
if(block == null) return;
blocks[getIndex(x, y, z)] = block;
int index = getIndex(x, y, z);
if(index > blocks.length || index < 0) return;
blocks[index] = minestomBlockState;
}
private int getIndex(int x, int y, int z) {
@@ -42,7 +45,9 @@ public class CachedChunk implements ProtoChunk {
@Override
public @NotNull BlockState getBlock(int x, int y, int z) {
return new MinestomBlockState(blocks[getIndex(x, y, z)]);
int index = getIndex(x, y, z);
if(index > blocks.length || index < 0) return MinestomBlockState.AIR;
return blocks[index];
}
@Override

Some files were not shown because too many files have changed in this diff Show More