mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 23:47:21 +00:00
Merge branch 'dev' into feat/kts
# Conflicts: # build.gradle # core/src/main/java/com/volmit/iris/Iris.java # core/src/main/java/com/volmit/iris/util/io/IO.java
This commit is contained in:
commit
73787e21d2
274
build.gradle
274
build.gradle
@ -1,274 +0,0 @@
|
||||
import xyz.jpenilla.runpaper.task.RunServer
|
||||
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
buildscript() {
|
||||
repositories {
|
||||
maven { url 'https://jitpack.io'}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.VolmitSoftware:NMSTools:1.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'java-library'
|
||||
id "io.github.goooler.shadow" version "8.1.7"
|
||||
id "de.undercouch.download" version "5.0.1"
|
||||
id "xyz.jpenilla.run-paper" version "2.3.1"
|
||||
}
|
||||
|
||||
|
||||
version '3.6.6-1.20.1-1.21.4'
|
||||
|
||||
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||
// ======================== WINDOWS =============================
|
||||
registerCustomOutputTask('Cyberpwn', 'C://Users/cyberpwn/Documents/development/server/plugins')
|
||||
registerCustomOutputTask('Psycho', 'C://Dan/MinecraftDevelopment/Server/plugins')
|
||||
registerCustomOutputTask('ArcaneArts', 'C://Users/arcane/Documents/development/server/plugins')
|
||||
registerCustomOutputTask('Coco', 'D://mcsm/plugins')
|
||||
registerCustomOutputTask('Strange', 'D://Servers/1.17 Test Server/plugins')
|
||||
registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.19.4/plugins')
|
||||
registerCustomOutputTask('CrazyDev22', 'C://Users/Julian/Desktop/server/plugins')
|
||||
registerCustomOutputTask('PixelFury', 'C://Users/repix/workplace/Iris/1.21.3 - Development-Public-v3/plugins')
|
||||
registerCustomOutputTask('PixelFuryDev', 'C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins')
|
||||
// ========================== UNIX ==============================
|
||||
registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins')
|
||||
registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Developer/RemoteGit/Server/plugins')
|
||||
registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins')
|
||||
registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins')
|
||||
// ==============================================================
|
||||
|
||||
def MIN_HEAP_SIZE = "2G"
|
||||
def MAX_HEAP_SIZE = "8G"
|
||||
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
|
||||
def COLOR = "truecolor"
|
||||
|
||||
def NMS_BINDINGS = Map.of(
|
||||
"v1_21_R3", "1.21.4-R0.1-SNAPSHOT",
|
||||
"v1_21_R2", "1.21.3-R0.1-SNAPSHOT",
|
||||
"v1_21_R1", "1.21.1-R0.1-SNAPSHOT",
|
||||
"v1_20_R4", "1.20.6-R0.1-SNAPSHOT",
|
||||
"v1_20_R3", "1.20.4-R0.1-SNAPSHOT",
|
||||
"v1_20_R2", "1.20.2-R0.1-SNAPSHOT",
|
||||
"v1_20_R1", "1.20.1-R0.1-SNAPSHOT",
|
||||
)
|
||||
def JVM_VERSION = Map.<String, Integer>of()
|
||||
NMS_BINDINGS.forEach { key, value ->
|
||||
project(":nms:$key") {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.volmit.nmstools'
|
||||
|
||||
nmsTools {
|
||||
it.jvm = JVM_VERSION.getOrDefault(key, 21)
|
||||
it.version = value
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":core")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("runServer-$key", RunServer) {
|
||||
group("servers")
|
||||
minecraftVersion(value.split("-")[0])
|
||||
minHeapSize(MIN_HEAP_SIZE)
|
||||
maxHeapSize(MAX_HEAP_SIZE)
|
||||
pluginJars(tasks.shadowJar.archiveFile)
|
||||
javaLauncher = javaToolchains.launcherFor { it.languageVersion = JavaLanguageVersion.of(JVM_VERSION.getOrDefault(key, 21))}
|
||||
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
|
||||
systemProperty("disable.watchdog", "")
|
||||
systemProperty("net.kyori.ansi.colorLevel", COLOR)
|
||||
systemProperty("com.mojang.eula.agree", true)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
NMS_BINDINGS.each {
|
||||
dependsOn(":nms:${it.key}:remap")
|
||||
from("${project(":nms:${it.key}").layout.buildDirectory.asFile.get()}/libs/${it.key}-mapped.jar")
|
||||
}
|
||||
|
||||
//minimize()
|
||||
append("plugin.yml")
|
||||
relocate 'com.dfsek.paralithic', 'com.volmit.iris.util.paralithic'
|
||||
relocate 'io.papermc.lib', 'com.volmit.iris.util.paper'
|
||||
relocate 'net.kyori', 'com.volmit.iris.util.kyori'
|
||||
relocate 'org.bstats', 'com.volmit.util.metrics'
|
||||
archiveFileName.set("Iris-${project.version}.jar")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':core')
|
||||
}
|
||||
|
||||
configurations.configureEach {
|
||||
resolutionStrategy.cacheChangingModulesFor 60, 'minutes'
|
||||
resolutionStrategy.cacheDynamicVersionsFor 60, 'minutes'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "https://repo.papermc.io/repository/maven-public/" }
|
||||
maven { url "https://repo.codemc.org/repository/maven-public" }
|
||||
maven { url "https://mvn.lumine.io/repository/maven-public/" }
|
||||
maven { url "https://jitpack.io" }
|
||||
|
||||
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" }
|
||||
maven { url "https://mvn.lumine.io/repository/maven/" }
|
||||
maven { url "https://repo.triumphteam.dev/snapshots" }
|
||||
maven { url "https://repo.mineinabyss.com/releases" }
|
||||
maven { url 'https://hub.jeff-media.com/nexus/repository/jeff-media-public/' }
|
||||
maven { url "https://repo.nexomc.com/snapshots/" }
|
||||
maven { url "https://libraries.minecraft.net" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Provided or Classpath
|
||||
compileOnly 'org.projectlombok:lombok:1.18.36'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
||||
|
||||
// Shaded
|
||||
implementation 'com.dfsek:paralithic:0.8.1'
|
||||
implementation 'io.papermc:paperlib:1.0.5'
|
||||
implementation "net.kyori:adventure-text-minimessage:4.17.0"
|
||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
||||
implementation 'net.kyori:adventure-api:4.17.0'
|
||||
implementation 'org.bstats:bstats-bukkit:3.1.0'
|
||||
//implementation 'org.bytedeco:javacpp:1.5.10'
|
||||
//implementation 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10'
|
||||
compileOnly 'io.lumine:Mythic-Dist:5.2.1'
|
||||
compileOnly 'io.lumine:MythicCrucible-Dist:2.0.0'
|
||||
|
||||
// Dynamically Loaded
|
||||
compileOnly 'io.timeandspace:smoothie-map:2.0.2'
|
||||
compileOnly 'it.unimi.dsi:fastutil:8.5.8'
|
||||
compileOnly 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2'
|
||||
compileOnly 'org.zeroturnaround:zt-zip:1.14'
|
||||
compileOnly 'com.google.code.gson:gson:2.10.1'
|
||||
compileOnly 'org.ow2.asm:asm:9.2'
|
||||
compileOnly 'com.google.guava:guava:33.0.0-jre'
|
||||
compileOnly 'com.github.ben-manes.caffeine:caffeine:3.0.6'
|
||||
compileOnly 'org.apache.commons:commons-lang3:3.12.0'
|
||||
compileOnly 'com.github.oshi:oshi-core:6.6.5'
|
||||
compileOnly 'org.dom4j:dom4j:2.1.4'
|
||||
}
|
||||
|
||||
/**
|
||||
* We need parameter meta for the decree command system
|
||||
*/
|
||||
compileJava {
|
||||
options.compilerArgs << '-parameters'
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.encoding = "UTF-8"
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
archiveClassifier.set('javadoc')
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
}
|
||||
|
||||
if (JavaVersion.current().toString() != "21") {
|
||||
System.err.println()
|
||||
System.err.println("=========================================================================================================")
|
||||
System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current())
|
||||
System.err.println()
|
||||
System.err.println("=== For IDEs ===")
|
||||
System.err.println("1. Configure the project for Java 21")
|
||||
System.err.println("2. Configure the bundled gradle to use Java 21 in settings")
|
||||
System.err.println()
|
||||
System.err.println("=== For Command Line (gradlew) ===")
|
||||
System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html")
|
||||
System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-21.0.4")
|
||||
System.err.println("3. Open a new command prompt window to get the new environment variables if need be.")
|
||||
System.err.println("=========================================================================================================")
|
||||
System.err.println()
|
||||
System.exit(69);
|
||||
}
|
||||
|
||||
task iris(type: Copy) {
|
||||
group "iris"
|
||||
from new File(layout.buildDirectory.asFile.get(), "libs/Iris-${version}.jar")
|
||||
into layout.buildDirectory.asFile.get()
|
||||
dependsOn(build)
|
||||
}
|
||||
|
||||
// with classifier: 'javadoc' and 'sources'
|
||||
task irisDev(type: Copy) {
|
||||
group "iris"
|
||||
from("core/build/libs/core-javadoc.jar", "core/build/libs/core-sources.jar")
|
||||
rename { String fileName ->
|
||||
fileName.replace("core", "Iris-${version}")
|
||||
}
|
||||
into layout.buildDirectory.asFile.get()
|
||||
dependsOn(iris)
|
||||
dependsOn("core:sourcesJar")
|
||||
dependsOn("core:javadocJar")
|
||||
}
|
||||
|
||||
|
||||
def registerCustomOutputTask(name, path) {
|
||||
if (!System.properties['os.name'].toLowerCase().contains('windows')) {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.register('build' + name, Copy) {
|
||||
group('development')
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn(iris)
|
||||
from(new File(buildDir, "Iris-" + version + ".jar"))
|
||||
into(file(path))
|
||||
rename { String fileName ->
|
||||
fileName.replace("Iris-" + version + ".jar", "Iris.jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def registerCustomOutputTaskUnix(name, path) {
|
||||
if (System.properties['os.name'].toLowerCase().contains('windows')) {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.register('build' + name, Copy) {
|
||||
group('development')
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn(iris)
|
||||
from(new File(buildDir, "Iris-" + version + ".jar"))
|
||||
into(file(path))
|
||||
rename { String fileName ->
|
||||
fileName.replace("Iris-" + version + ".jar", "Iris.jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.build.dependsOn(shadowJar)
|
278
build.gradle.kts
Normal file
278
build.gradle.kts
Normal file
@ -0,0 +1,278 @@
|
||||
import com.volmit.nmstools.NMSToolsExtension
|
||||
import com.volmit.nmstools.NMSToolsPlugin
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import xyz.jpenilla.runpaper.task.RunServer
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories.maven("https://jitpack.io")
|
||||
dependencies.classpath("com.github.VolmitSoftware:NMSTools:c5cbc46ce6")
|
||||
}
|
||||
|
||||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
id("com.gradleup.shadow") version "8.3.6"
|
||||
id("de.undercouch.download") version "5.0.1"
|
||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
||||
id("io.sentry.jvm.gradle") version "5.7.0"
|
||||
}
|
||||
|
||||
version = "3.6.11-1.20.1-1.21.5"
|
||||
|
||||
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||
// ======================== WINDOWS =============================
|
||||
registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins")
|
||||
registerCustomOutputTask("Psycho", "C://Dan/MinecraftDevelopment/Server/plugins")
|
||||
registerCustomOutputTask("ArcaneArts", "C://Users/arcane/Documents/development/server/plugins")
|
||||
registerCustomOutputTask("Coco", "D://mcsm/plugins")
|
||||
registerCustomOutputTask("Strange", "D://Servers/1.17 Test Server/plugins")
|
||||
registerCustomOutputTask("Vatuu", "D://Minecraft/Servers/1.19.4/plugins")
|
||||
registerCustomOutputTask("CrazyDev22", "C://Users/Julian/Desktop/server/plugins")
|
||||
registerCustomOutputTask("PixelFury", "C://Users/repix/workplace/Iris/1.21.3 - Development-Public-v3/plugins")
|
||||
registerCustomOutputTask("PixelFuryDev", "C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins")
|
||||
// ========================== UNIX ==============================
|
||||
registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins")
|
||||
registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/Server/plugins")
|
||||
registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
|
||||
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
|
||||
// ==============================================================
|
||||
|
||||
val serverMinHeap = "2G"
|
||||
val serverMaxHeap = "8G"
|
||||
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
|
||||
val color = "truecolor"
|
||||
val errorReporting = false
|
||||
|
||||
val nmsBindings = mapOf(
|
||||
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
||||
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
||||
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
||||
"v1_21_R1" to "1.21.1-R0.1-SNAPSHOT",
|
||||
"v1_20_R4" to "1.20.6-R0.1-SNAPSHOT",
|
||||
"v1_20_R3" to "1.20.4-R0.1-SNAPSHOT",
|
||||
"v1_20_R2" to "1.20.2-R0.1-SNAPSHOT",
|
||||
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
|
||||
)
|
||||
val jvmVersion = mapOf<String, Int>()
|
||||
nmsBindings.forEach { key, value ->
|
||||
project(":nms:$key") {
|
||||
apply<JavaPlugin>()
|
||||
apply<NMSToolsPlugin>()
|
||||
|
||||
repositories {
|
||||
maven("https://libraries.minecraft.net")
|
||||
}
|
||||
|
||||
extensions.configure(NMSToolsExtension::class) {
|
||||
jvm = jvmVersion.getOrDefault(key, 21)
|
||||
version = value
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":core"))
|
||||
compileOnly("org.jetbrains:annotations:26.0.2")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<RunServer>("runServer-$key") {
|
||||
group = "servers"
|
||||
minecraftVersion(value.split("-")[0])
|
||||
minHeapSize = serverMinHeap
|
||||
maxHeapSize = serverMaxHeap
|
||||
pluginJars(tasks.jar.flatMap { it.archiveFile })
|
||||
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))}
|
||||
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
|
||||
systemProperty("disable.watchdog", "")
|
||||
systemProperty("net.kyori.ansi.colorLevel", color)
|
||||
systemProperty("com.mojang.eula.agree", true)
|
||||
systemProperty("iris.errorReporting", errorReporting)
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
jar {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
nmsBindings.forEach { key, _ ->
|
||||
from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) })
|
||||
}
|
||||
from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
|
||||
archiveFileName.set("Iris-${project.version}.jar")
|
||||
}
|
||||
|
||||
register<Copy>("iris") {
|
||||
group = "iris"
|
||||
dependsOn("jar")
|
||||
from(layout.buildDirectory.file("libs/Iris-${project.version}.jar"))
|
||||
into(layout.buildDirectory)
|
||||
}
|
||||
|
||||
register<Copy>("irisDev") {
|
||||
group = "iris"
|
||||
from(project(":core").layout.buildDirectory.files("libs/core-javadoc.jar", "libs/core-sources.jar"))
|
||||
rename { it.replace("core", "Iris-${project.version}") }
|
||||
into(layout.buildDirectory)
|
||||
dependsOn(":core:sourcesJar")
|
||||
dependsOn(":core:javadocJar")
|
||||
}
|
||||
|
||||
val cli = file("sentry-cli.exe")
|
||||
register<Download>("downloadCli") {
|
||||
group = "io.sentry"
|
||||
src("https://release-registry.services.sentry.io/apps/sentry-cli/latest?response=download&arch=x86_64&platform=${System.getProperty("os.name")}&package=sentry-cli")
|
||||
dest(cli)
|
||||
|
||||
doLast {
|
||||
cli.setExecutable(true)
|
||||
}
|
||||
}
|
||||
|
||||
register("release") {
|
||||
group = "io.sentry"
|
||||
dependsOn("downloadCli")
|
||||
doLast {
|
||||
val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN")
|
||||
val org = "volmit-software"
|
||||
val projectName = "iris"
|
||||
exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version)
|
||||
exec(cli, "releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto", "--ignore-missing")
|
||||
exec(cli, "releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version)
|
||||
cli.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exec(vararg command: Any) {
|
||||
val p = ProcessBuilder(command.map { it.toString() })
|
||||
.start()
|
||||
p.inputStream.reader().useLines { it.forEach(::println) }
|
||||
p.errorStream.reader().useLines { it.forEach(::println) }
|
||||
p.waitFor()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
}
|
||||
|
||||
configurations.configureEach {
|
||||
resolutionStrategy.cacheChangingModulesFor(60, "minutes")
|
||||
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.codemc.org/repository/maven-public/")
|
||||
maven("https://mvn.lumine.io/repository/maven-public/")
|
||||
maven("https://jitpack.io")
|
||||
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots")
|
||||
maven("https://mvn.lumine.io/repository/maven/")
|
||||
maven("https://repo.triumphteam.dev/snapshots")
|
||||
maven("https://repo.mineinabyss.com/releases")
|
||||
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
|
||||
maven("https://repo.nexomc.com/releases/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Provided or Classpath
|
||||
compileOnly("org.projectlombok:lombok:1.18.36")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.36")
|
||||
}
|
||||
|
||||
/**
|
||||
* We need parameter meta for the decree command system
|
||||
*/
|
||||
tasks {
|
||||
compileJava {
|
||||
options.compilerArgs.add("-parameters")
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.encoding = "UTF-8"
|
||||
options.quiet()
|
||||
//options.addStringOption("Xdoclint:none") // TODO: Re-enable this
|
||||
}
|
||||
|
||||
register<Jar>("sourcesJar") {
|
||||
archiveClassifier.set("sources")
|
||||
from(sourceSets.main.map { it.allSource })
|
||||
}
|
||||
|
||||
register<Jar>("javadocJar") {
|
||||
archiveClassifier.set("javadoc")
|
||||
from(javadoc.map { it.destinationDir!! })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JavaVersion.current().toString() != "21") {
|
||||
System.err.println()
|
||||
System.err.println("=========================================================================================================")
|
||||
System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current())
|
||||
System.err.println()
|
||||
System.err.println("=== For IDEs ===")
|
||||
System.err.println("1. Configure the project for Java 21")
|
||||
System.err.println("2. Configure the bundled gradle to use Java 21 in settings")
|
||||
System.err.println()
|
||||
System.err.println("=== For Command Line (gradlew) ===")
|
||||
System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html")
|
||||
System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-21.0.4")
|
||||
System.err.println("3. Open a new command prompt window to get the new environment variables if need be.")
|
||||
System.err.println("=========================================================================================================")
|
||||
System.err.println()
|
||||
exitProcess(69)
|
||||
}
|
||||
|
||||
|
||||
fun registerCustomOutputTask(name: String, path: String) {
|
||||
if (!System.getProperty("os.name").lowercase().contains("windows")) {
|
||||
return
|
||||
}
|
||||
|
||||
tasks.register<Copy>("build$name") {
|
||||
group = "development"
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn("iris")
|
||||
from(layout.buildDirectory.file("Iris-${project.version}.jar"))
|
||||
into(file(path))
|
||||
rename { "Iris.jar" }
|
||||
}
|
||||
}
|
||||
|
||||
fun registerCustomOutputTaskUnix(name: String, path: String) {
|
||||
if (System.getProperty("os.name").lowercase().contains("windows")) {
|
||||
return
|
||||
}
|
||||
|
||||
tasks.register<Copy>("build$name") {
|
||||
group = "development"
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn("iris")
|
||||
from(layout.buildDirectory.file("Iris-${project.version}.jar"))
|
||||
into(file(path))
|
||||
rename { "Iris.jar" }
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'java-library'
|
||||
id "io.freefair.lombok" version "8.6"
|
||||
}
|
||||
|
||||
def apiVersion = '1.19'
|
||||
def main = 'com.volmit.iris.Iris'
|
||||
|
||||
/**
|
||||
* We need parameter meta for the decree command system
|
||||
*/
|
||||
compileJava {
|
||||
options.compilerArgs << '-parameters'
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'https://nexus.phoenixdevt.fr/repository/maven-public/'}
|
||||
maven { url 'https://repo.auxilor.io/repository/maven-public/' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*
|
||||
* Provided or classpath dependencies are not shaded and are available on the runtime classpath
|
||||
*
|
||||
* Shaded dependencies are not available at runtime, nor are they available on mvn central so they
|
||||
* need to be shaded into the jar (increasing binary size)
|
||||
*
|
||||
* Dynamically loaded dependencies are defined in the plugin.yml (updating these must be updated in the
|
||||
* plugin.yml also, otherwise they wont be available). These do not increase binary size). Only declare
|
||||
* these dependencies if they are available on mvn central.
|
||||
*/
|
||||
dependencies {
|
||||
// Provided or Classpath
|
||||
compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.apache.logging.log4j:log4j-api:2.19.0'
|
||||
compileOnly 'org.apache.logging.log4j:log4j-core:2.19.0'
|
||||
compileOnly 'commons-io:commons-io:2.13.0'
|
||||
compileOnly 'commons-lang:commons-lang:2.6'
|
||||
compileOnly 'com.github.oshi:oshi-core:5.8.5'
|
||||
compileOnly 'org.lz4:lz4-java:1.8.0'
|
||||
|
||||
// Third Party Integrations
|
||||
compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7'
|
||||
compileOnly 'com.nexomc:nexo:1.0.0-dev.38'
|
||||
compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4'
|
||||
compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3'
|
||||
compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8'
|
||||
compileOnly 'net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT'
|
||||
compileOnly 'com.willfp:EcoItems:5.44.0'
|
||||
//implementation files('libs/CustomItems.jar')
|
||||
}
|
||||
|
||||
java {
|
||||
disableAutoTargetJvm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly.
|
||||
*/
|
||||
file(jar.archiveFile.get().getAsFile().getParentFile().getParentFile().getParentFile().getAbsolutePath() + '/build/resources/main/plugin.yml').delete()
|
||||
|
||||
/**
|
||||
* Expand properties into plugin yml
|
||||
*/
|
||||
processResources {
|
||||
filesMatching('**/plugin.yml') {
|
||||
expand(
|
||||
'name': rootProject.name.toString(),
|
||||
'version': rootProject.version.toString(),
|
||||
'main': main.toString(),
|
||||
'apiversion': apiVersion.toString()
|
||||
)
|
||||
}
|
||||
}
|
150
core/build.gradle.kts
Normal file
150
core/build.gradle.kts
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
id("com.gradleup.shadow")
|
||||
id("io.sentry.jvm.gradle")
|
||||
}
|
||||
|
||||
val apiVersion = "1.19"
|
||||
val main = "com.volmit.iris.Iris"
|
||||
|
||||
repositories {
|
||||
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
|
||||
maven("https://repo.auxilor.io/repository/maven-public/")
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*
|
||||
* Provided or classpath dependencies are not shaded and are available on the runtime classpath
|
||||
*
|
||||
* Shaded dependencies are not available at runtime, nor are they available on mvn central so they
|
||||
* need to be shaded into the jar (increasing binary size)
|
||||
*
|
||||
* Dynamically loaded dependencies are defined in the plugin.yml (updating these must be updated in the
|
||||
* plugin.yml also, otherwise they wont be available). These do not increase binary size). Only declare
|
||||
* these dependencies if they are available on mvn central.
|
||||
*/
|
||||
dependencies {
|
||||
// Provided or Classpath
|
||||
compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT")
|
||||
compileOnly("org.apache.logging.log4j:log4j-api:2.19.0")
|
||||
compileOnly("org.apache.logging.log4j:log4j-core:2.19.0")
|
||||
compileOnly("commons-io:commons-io:2.13.0")
|
||||
compileOnly("commons-lang:commons-lang:2.6")
|
||||
compileOnly("com.github.oshi:oshi-core:5.8.5")
|
||||
compileOnly("org.lz4:lz4-java:1.8.0")
|
||||
|
||||
// Third Party Integrations
|
||||
compileOnly("com.nexomc:nexo:1.6.0")
|
||||
compileOnly("com.github.LoneDev6:api-itemsadder:3.4.1-r4")
|
||||
compileOnly("com.github.PlaceholderAPI:placeholderapi:2.11.3")
|
||||
compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8")
|
||||
compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT")
|
||||
compileOnly("com.willfp:EcoItems:5.44.0")
|
||||
//implementation files("libs/CustomItems.jar")
|
||||
|
||||
|
||||
// Shaded
|
||||
implementation("com.dfsek:paralithic:0.8.1")
|
||||
implementation("io.papermc:paperlib:1.0.5")
|
||||
implementation("net.kyori:adventure-text-minimessage:4.17.0")
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
|
||||
implementation("net.kyori:adventure-api:4.17.0")
|
||||
implementation("org.bstats:bstats-bukkit:3.1.0")
|
||||
|
||||
//implementation("org.bytedeco:javacpp:1.5.10")
|
||||
//implementation("org.bytedeco:cuda-platform:12.3-8.9-1.5.10")
|
||||
compileOnly("io.lumine:Mythic-Dist:5.2.1")
|
||||
compileOnly("io.lumine:MythicCrucible-Dist:2.0.0")
|
||||
|
||||
// Dynamically Loaded
|
||||
compileOnly("io.timeandspace:smoothie-map:2.0.2")
|
||||
compileOnly("it.unimi.dsi:fastutil:8.5.8")
|
||||
compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
|
||||
compileOnly("org.zeroturnaround:zt-zip:1.14")
|
||||
compileOnly("com.google.code.gson:gson:2.10.1")
|
||||
compileOnly("org.ow2.asm:asm:9.2")
|
||||
compileOnly("com.google.guava:guava:33.0.0-jre")
|
||||
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6")
|
||||
compileOnly("org.apache.commons:commons-lang3:3.12.0")
|
||||
compileOnly("com.github.oshi:oshi-core:6.6.5")
|
||||
compileOnly("org.dom4j:dom4j:2.1.4")
|
||||
}
|
||||
|
||||
java {
|
||||
disableAutoTargetJvm()
|
||||
}
|
||||
|
||||
sentry {
|
||||
includeSourceContext = true
|
||||
|
||||
org = "volmit-software"
|
||||
projectName = "iris"
|
||||
authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN")
|
||||
}
|
||||
|
||||
tasks {
|
||||
/**
|
||||
* We need parameter meta for the decree command system
|
||||
*/
|
||||
compileJava {
|
||||
options.compilerArgs.add("-parameters")
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand properties into plugin yml
|
||||
*/
|
||||
processResources {
|
||||
inputs.properties(
|
||||
"name" to rootProject.name,
|
||||
"version" to rootProject.version,
|
||||
"apiVersion" to apiVersion,
|
||||
"main" to main
|
||||
)
|
||||
filesMatching("**/plugin.yml") {
|
||||
expand(inputs.properties)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
mergeServiceFiles()
|
||||
relocate("com.dfsek.paralithic", "com.volmit.iris.util.paralithic")
|
||||
relocate("io.papermc.lib", "com.volmit.iris.util.paper")
|
||||
relocate("net.kyori", "com.volmit.iris.util.kyori")
|
||||
relocate("org.bstats", "com.volmit.iris.util.metrics")
|
||||
relocate("io.sentry", "com.volmit.iris.util.sentry")
|
||||
|
||||
//minimize()
|
||||
dependencies {
|
||||
exclude(dependency("org.ow2.asm:asm:"))
|
||||
exclude(dependency("org.jetbrains:"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly.
|
||||
*/
|
||||
afterEvaluate {
|
||||
layout.buildDirectory.file("resources/main/plugin.yml").get().asFile.delete()
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.json.JSONException;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -45,6 +46,7 @@ public class IrisSettings {
|
||||
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
|
||||
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
|
||||
private IrisSettingsPregen pregen = new IrisSettingsPregen();
|
||||
private IrisSettingsSentry sentry = new IrisSettingsSentry();
|
||||
|
||||
public static int getThreadCount(int c) {
|
||||
return switch (c) {
|
||||
@ -130,28 +132,46 @@ public class IrisSettings {
|
||||
public boolean markerEntitySpawningSystem = true;
|
||||
public boolean effectSystem = true;
|
||||
public boolean worldEditWandCUI = true;
|
||||
public boolean globalPregenCache = true;
|
||||
public boolean globalPregenCache = false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsConcurrency {
|
||||
public int parallelism = -1;
|
||||
public int worldGenParallelism = -1;
|
||||
|
||||
public int getWorldGenThreads() {
|
||||
return getThreadCount(worldGenParallelism);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsPregen {
|
||||
public boolean useCacheByDefault = true;
|
||||
public boolean useHighPriority = false;
|
||||
public boolean useVirtualThreads = false;
|
||||
public boolean useTicketQueue = false;
|
||||
public int maxConcurrency = 256;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsPerformance {
|
||||
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
|
||||
public boolean trimMantleInStudio = false;
|
||||
public int mantleKeepAlive = 30;
|
||||
public int cacheSize = 4_096;
|
||||
public int resourceLoaderCacheSize = 1_024;
|
||||
public int objectLoaderCacheSize = 4_096;
|
||||
public int scriptLoaderCacheSize = 512;
|
||||
public int tectonicPlateSize = -1;
|
||||
public int mantleCleanupDelay = 200;
|
||||
|
||||
public int getTectonicPlateSize() {
|
||||
if (tectonicPlateSize > 0)
|
||||
return tectonicPlateSize;
|
||||
|
||||
return (int) (getHardware.getProcessMemory() / 200L);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@ -183,6 +203,7 @@ public class IrisSettings {
|
||||
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
||||
public boolean commandSounds = true;
|
||||
public boolean debug = false;
|
||||
public boolean dumpMantleOnError = false;
|
||||
public boolean disableNMS = false;
|
||||
public boolean pluginMetrics = true;
|
||||
public boolean splashLogoStartup = true;
|
||||
@ -202,6 +223,13 @@ public class IrisSettings {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsSentry {
|
||||
public boolean includeServerId = true;
|
||||
public boolean disableAutoReporting = false;
|
||||
public boolean debug = false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsGUI {
|
||||
public boolean useServerLaunchedGuis = true;
|
||||
@ -223,4 +251,14 @@ public class IrisSettings {
|
||||
public boolean disableTimeAndWeather = true;
|
||||
public boolean autoStartDefaultStudio = false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsEngineSVC {
|
||||
public boolean useVirtualThreads = true;
|
||||
public int priority = Thread.NORM_PRIORITY;
|
||||
|
||||
public int getPriority() {
|
||||
return Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,10 +71,12 @@ public class ServerConfigurator {
|
||||
f.load(spigotConfig);
|
||||
long tt = f.getLong("settings.timeout-time");
|
||||
|
||||
if (tt < TimeUnit.MINUTES.toSeconds(5)) {
|
||||
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)");
|
||||
long spigotTimeout = TimeUnit.MINUTES.toSeconds(5);
|
||||
|
||||
if (tt < spigotTimeout) {
|
||||
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + spigotTimeout + " (5 minutes)");
|
||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||
f.set("settings.timeout-time", TimeUnit.MINUTES.toSeconds(5));
|
||||
f.set("settings.timeout-time", spigotTimeout);
|
||||
f.save(spigotConfig);
|
||||
}
|
||||
}
|
||||
@ -84,10 +86,11 @@ public class ServerConfigurator {
|
||||
f.load(spigotConfig);
|
||||
long tt = f.getLong("watchdog.early-warning-delay");
|
||||
|
||||
if (tt < TimeUnit.MINUTES.toMillis(3)) {
|
||||
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)");
|
||||
long watchdog = TimeUnit.MINUTES.toMillis(3);
|
||||
if (tt < watchdog) {
|
||||
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + watchdog + " (3 minutes)");
|
||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||
f.set("watchdog.early-warning-delay", TimeUnit.MINUTES.toMillis(3));
|
||||
f.set("watchdog.early-warning-delay", watchdog);
|
||||
f.save(spigotConfig);
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ package com.volmit.iris.core.commands;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.ServerConfigurator;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.util.context.IrisContext;
|
||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||
import com.volmit.iris.util.decree.annotations.Decree;
|
||||
@ -68,53 +68,15 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
|
||||
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
||||
public void EngineStatus() {
|
||||
List<World> IrisWorlds = new ArrayList<>();
|
||||
int TotalLoadedChunks = 0;
|
||||
int TotalQueuedTectonicPlates = 0;
|
||||
int TotalNotQueuedTectonicPlates = 0;
|
||||
int TotalTectonicPlates = 0;
|
||||
Iris.service(IrisEngineSVC.class)
|
||||
.engineStatus(sender());
|
||||
}
|
||||
|
||||
long lowestUnloadDuration = 0;
|
||||
long highestUnloadDuration = 0;
|
||||
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
try {
|
||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
||||
IrisWorlds.add(world);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// no
|
||||
}
|
||||
}
|
||||
|
||||
for (World world : IrisWorlds) {
|
||||
Engine engine = IrisToolbelt.access(world).getEngine();
|
||||
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
|
||||
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
|
||||
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
|
||||
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
|
||||
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
||||
}
|
||||
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
|
||||
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
||||
}
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
if (chunk.isLoaded()) {
|
||||
TotalLoadedChunks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Iris.info("-------------------------");
|
||||
Iris.info(C.DARK_PURPLE + "Engine Status");
|
||||
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
|
||||
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
|
||||
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||
Iris.info("-------------------------");
|
||||
@Decree(description = "Send a test exception to sentry")
|
||||
public void Sentry() {
|
||||
Engine engine = engine();
|
||||
if (engine != null) IrisContext.getOr(engine);
|
||||
Iris.reportError(new Exception("This is a test"));
|
||||
}
|
||||
|
||||
@Decree(description = "Test")
|
||||
@ -166,7 +128,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
|
||||
File tectonicplates = new File(folder, "mantle");
|
||||
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
|
||||
TectonicPlate.read(maxHeight, i);
|
||||
TectonicPlate.read(maxHeight, i, true);
|
||||
c++;
|
||||
Iris.info("Loaded count: " + c );
|
||||
|
||||
@ -272,7 +234,8 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
@Param(description = "base IrisWorld") World world,
|
||||
@Param(description = "raw TectonicPlate File") String path,
|
||||
@Param(description = "Algorithm to Test") String algorithm,
|
||||
@Param(description = "Amount of Tests") int amount) {
|
||||
@Param(description = "Amount of Tests") int amount,
|
||||
@Param(description = "Is versioned", defaultValue = "false") boolean versioned) {
|
||||
if (!IrisToolbelt.isIrisWorld(world)) {
|
||||
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
|
||||
return;
|
||||
@ -289,7 +252,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
service.submit(() -> {
|
||||
try {
|
||||
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
|
||||
TectonicPlate plate = new TectonicPlate(height, raw);
|
||||
TectonicPlate plate = new TectonicPlate(height, raw, versioned);
|
||||
raw.close();
|
||||
|
||||
double d1 = 0;
|
||||
@ -308,7 +271,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
size = tmp.length();
|
||||
start = System.currentTimeMillis();
|
||||
CountingDataInputStream din = createInput(tmp, algorithm);
|
||||
new TectonicPlate(height, din);
|
||||
new TectonicPlate(height, din, true);
|
||||
din.close();
|
||||
d2 += System.currentTimeMillis() - start;
|
||||
tmp.delete();
|
||||
|
@ -72,7 +72,6 @@ public class CommandIris implements DecreeExecutor {
|
||||
private CommandWhat what;
|
||||
private CommandEdit edit;
|
||||
private CommandFind find;
|
||||
private CommandSupport support;
|
||||
private CommandDeveloper developer;
|
||||
public static boolean worldCreation = false;
|
||||
String WorldEngine;
|
||||
|
@ -241,7 +241,8 @@ public class CommandObject implements DecreeExecutor {
|
||||
|
||||
|
||||
Location[] b = WandSVC.getCuboid(player());
|
||||
if (b == null) {
|
||||
if (b == null || b[0] == null || b[1] == null) {
|
||||
sender().sendMessage("No area selected.");
|
||||
return;
|
||||
}
|
||||
Location a1 = b[0].clone();
|
||||
@ -417,6 +418,10 @@ public class CommandObject implements DecreeExecutor {
|
||||
}
|
||||
|
||||
Location[] b = WandSVC.getCuboid(player());
|
||||
if (b == null || b[0] == null || b[1] == null) {
|
||||
sender().sendMessage("No area selected.");
|
||||
return;
|
||||
}
|
||||
Location a1 = b[0].clone();
|
||||
Location a2 = b[1].clone();
|
||||
Direction d = Direction.closest(player().getLocation().getDirection()).reverse();
|
||||
@ -477,6 +482,10 @@ public class CommandObject implements DecreeExecutor {
|
||||
}
|
||||
|
||||
Location[] b = WandSVC.getCuboid(player());
|
||||
if (b == null || b[0] == null || b[1] == null) {
|
||||
sender().sendMessage("No area selected.");
|
||||
return;
|
||||
}
|
||||
Location a1 = b[0].clone();
|
||||
Location a2 = b[1].clone();
|
||||
Location a1x = b[0].clone();
|
||||
@ -524,6 +533,10 @@ public class CommandObject implements DecreeExecutor {
|
||||
}
|
||||
|
||||
Location[] b = WandSVC.getCuboid(player());
|
||||
if (b == null || b[0] == null || b[1] == null) {
|
||||
sender().sendMessage("No area selected.");
|
||||
return;
|
||||
}
|
||||
b[0].add(new Vector(0, 1, 0));
|
||||
b[1].add(new Vector(0, 1, 0));
|
||||
Location a1 = b[0].clone();
|
||||
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.core.commands;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||
import com.volmit.iris.util.decree.annotations.Decree;
|
||||
import com.volmit.iris.util.decree.annotations.Param;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||
import com.volmit.iris.util.misc.Hastebin;
|
||||
import com.volmit.iris.util.misc.Platform;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import oshi.SystemInfo;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"})
|
||||
public class CommandSupport implements DecreeExecutor {
|
||||
|
||||
@Decree(description = "report")
|
||||
public void report() {
|
||||
try {
|
||||
if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report..");
|
||||
if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report..");
|
||||
Hastebin.enviornment(sender());
|
||||
|
||||
} catch (Exception e) {
|
||||
Iris.info(C.RED + "Something went wrong: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -55,7 +56,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
|
||||
private static final Color COLOR_GENERATED = parseColor("#65c295");
|
||||
private static final Color COLOR_CLEANED = parseColor("#34eb93");
|
||||
public static PregeneratorJob instance;
|
||||
private static final AtomicReference<PregeneratorJob> instance = new AtomicReference<>();
|
||||
private final MemoryMonitor monitor;
|
||||
private final PregenTask task;
|
||||
private final boolean saving;
|
||||
@ -73,8 +74,14 @@ public class PregeneratorJob implements PregenListener {
|
||||
private String[] info;
|
||||
|
||||
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||
instance.updateAndGet(old -> {
|
||||
if (old != null) {
|
||||
old.pregenerator.close();
|
||||
old.close();
|
||||
}
|
||||
return this;
|
||||
});
|
||||
this.engine = engine;
|
||||
instance = this;
|
||||
monitor = new MemoryMonitor(50);
|
||||
saving = false;
|
||||
info = new String[]{"Initializing..."};
|
||||
@ -103,37 +110,40 @@ public class PregeneratorJob implements PregenListener {
|
||||
}
|
||||
|
||||
public static boolean shutdownInstance() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
J.a(() -> instance.pregenerator.close());
|
||||
J.a(inst.pregenerator::close);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static PregeneratorJob getInstance() {
|
||||
return instance;
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
public static boolean pauseResume() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPaused()) {
|
||||
instance.pregenerator.resume();
|
||||
inst.pregenerator.resume();
|
||||
} else {
|
||||
instance.pregenerator.pause();
|
||||
inst.pregenerator.pause();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPaused() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return instance.paused();
|
||||
return inst.paused();
|
||||
}
|
||||
|
||||
private static Color parseColor(String c) {
|
||||
@ -183,7 +193,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
J.a(() -> {
|
||||
pregenerator.close();
|
||||
close();
|
||||
instance = null;
|
||||
instance.compareAndSet(this, null);
|
||||
});
|
||||
}
|
||||
|
||||
@ -311,7 +321,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
@Override
|
||||
public void onClose() {
|
||||
close();
|
||||
instance = null;
|
||||
instance.compareAndSet(this, null);
|
||||
whenDone.forEach(Runnable::run);
|
||||
service.shutdownNow();
|
||||
}
|
||||
|
@ -18,20 +18,33 @@
|
||||
|
||||
package com.volmit.iris.core.link;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import io.lumine.mythic.api.adapters.AbstractLocation;
|
||||
import io.lumine.mythic.api.config.MythicLineConfig;
|
||||
import io.lumine.mythic.api.skills.conditions.ILocationCondition;
|
||||
import io.lumine.mythic.bukkit.MythicBukkit;
|
||||
import io.lumine.mythic.bukkit.adapters.BukkitWorld;
|
||||
import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent;
|
||||
import io.lumine.mythic.core.skills.SkillCondition;
|
||||
import io.lumine.mythic.core.utils.annotations.MythicCondition;
|
||||
import io.lumine.mythic.core.utils.annotations.MythicField;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class MythicMobsLink {
|
||||
|
||||
public MythicMobsLink() {
|
||||
|
||||
if (getPlugin() == null) return;
|
||||
Iris.instance.registerListener(new ConditionListener());
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@ -49,12 +62,70 @@ public class MythicMobsLink {
|
||||
* @param location The location
|
||||
* @return The mob, or null if it can't be spawned
|
||||
*/
|
||||
public @Nullable
|
||||
Entity spawnMob(String mob, Location location) {
|
||||
public @Nullable Entity spawnMob(String mob, Location location) {
|
||||
return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null;
|
||||
}
|
||||
|
||||
public Collection<String> getMythicMobTypes() {
|
||||
return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of();
|
||||
}
|
||||
|
||||
private static class ConditionListener implements Listener {
|
||||
@EventHandler
|
||||
public void on(MythicConditionLoadEvent event) {
|
||||
switch (event.getConditionName()) {
|
||||
case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig()));
|
||||
case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
|
||||
public static class IrisBiomeCondition extends SkillCondition implements ILocationCondition {
|
||||
@MythicField(name = "biome", aliases = {"b"}, description = "A list of biomes to check")
|
||||
private Set<String> biomes = Sets.newConcurrentHashSet();
|
||||
@MythicField(name = "surface", aliases = {"s"}, description = "If the biome check should only be performed on the surface")
|
||||
private boolean surface;
|
||||
|
||||
public IrisBiomeCondition(String line, MythicLineConfig mlc) {
|
||||
super(line);
|
||||
String b = mlc.getString(new String[]{"biome", "b"}, "");
|
||||
biomes.addAll(Arrays.asList(b.split(",")));
|
||||
surface = mlc.getBoolean(new String[]{"surface", "s"}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(AbstractLocation target) {
|
||||
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
|
||||
if (access == null) return false;
|
||||
var engine = access.getEngine();
|
||||
if (engine == null) return false;
|
||||
var biome = surface ?
|
||||
engine.getSurfaceBiome(target.getBlockX(), target.getBlockZ()) :
|
||||
engine.getBiomeOrMantle(target.getBlockX(), target.getBlockY() - engine.getMinHeight(), target.getBlockZ());
|
||||
return biomes.contains(biome.getLoadKey());
|
||||
}
|
||||
}
|
||||
|
||||
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
|
||||
public static class IrisRegionCondition extends SkillCondition implements ILocationCondition {
|
||||
@MythicField(name = "region", aliases = {"r"}, description = "A list of regions to check")
|
||||
private Set<String> regions = Sets.newConcurrentHashSet();
|
||||
|
||||
public IrisRegionCondition(String line, MythicLineConfig mlc) {
|
||||
super(line);
|
||||
String b = mlc.getString(new String[]{"region", "r"}, "");
|
||||
regions.addAll(Arrays.asList(b.split(",")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(AbstractLocation target) {
|
||||
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
|
||||
if (access == null) return false;
|
||||
var engine = access.getEngine();
|
||||
if (engine == null) return false;
|
||||
var region = engine.getRegion(target.getBlockX(), target.getBlockZ());
|
||||
return regions.contains(region.getLoadKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public class WorldEditLink {
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Could not get selection");
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
active.reset();
|
||||
active.aquire(() -> false);
|
||||
}
|
||||
|
@ -298,6 +298,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
|
||||
return r;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.error("Failed to create loader! " + registrant.getCanonicalName());
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ import lombok.ToString;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
@ -240,8 +241,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
||||
for (String i : s) {
|
||||
burst.queue(() -> {
|
||||
T t = load(i);
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
if (t != null) {
|
||||
synchronized (m) {
|
||||
m.add(t);
|
||||
}
|
||||
});
|
||||
|
@ -34,7 +34,8 @@ public class INMS {
|
||||
"1.21.1", "v1_21_R1",
|
||||
"1.21.2", "v1_21_R2",
|
||||
"1.21.3", "v1_21_R2",
|
||||
"1.21.4", "v1_21_R3"
|
||||
"1.21.4", "v1_21_R3",
|
||||
"1.21.5", "v1_21_R4"
|
||||
);
|
||||
private static final List<Version> PACKS = List.of(
|
||||
new Version(21, 4, "31020"),
|
||||
|
@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class ChunkUpdater {
|
||||
private static final String REGION_PATH = "region" + File.separator + "r.";
|
||||
private final AtomicBoolean paused = new AtomicBoolean();
|
||||
private final AtomicBoolean cancelled = new AtomicBoolean();
|
||||
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
|
||||
@ -108,6 +109,7 @@ public class ChunkUpdater {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, 0, 3, TimeUnit.SECONDS);
|
||||
@ -162,12 +164,12 @@ public class ChunkUpdater {
|
||||
J.sleep(50);
|
||||
}
|
||||
|
||||
if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) {
|
||||
return;
|
||||
}
|
||||
if (!new File(world.getWorldFolder(), "region" + File.separator + rX + "." + rZ + ".mca").exists()) {
|
||||
return;
|
||||
}
|
||||
if (rX < dimensions.min.getX() ||
|
||||
rX > dimensions.max.getX() ||
|
||||
rZ < dimensions.min.getZ() ||
|
||||
rZ > dimensions.max.getZ() ||
|
||||
!new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists()
|
||||
) return;
|
||||
|
||||
task.iterateChunks(rX, rZ, (x, z) -> {
|
||||
while (paused.get() && !cancelled.get()) {
|
||||
@ -313,6 +315,7 @@ public class ChunkUpdater {
|
||||
world.save();
|
||||
}).get();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,10 @@ public class IrisPregenerator {
|
||||
private final KSet<Position2> net;
|
||||
private final ChronoLatch cl;
|
||||
private final ChronoLatch saveLatch = new ChronoLatch(30000);
|
||||
private final IrisPackBenchmarking benchmarking;
|
||||
|
||||
public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) {
|
||||
benchmarking = IrisPackBenchmarking.getInstance();
|
||||
this.listener = listenify(listener);
|
||||
cl = new ChronoLatch(5000);
|
||||
generatedRegions = new KSet<>();
|
||||
@ -135,7 +137,7 @@ public class IrisPregenerator {
|
||||
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
|
||||
|
||||
Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
|
||||
IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen",
|
||||
benchmarking != null ? "Benchmarking" : "Pregen",
|
||||
Form.f(generated.get()),
|
||||
Form.f(totalChunks.get()),
|
||||
percentage,
|
||||
@ -174,10 +176,10 @@ public class IrisPregenerator {
|
||||
task.iterateRegions((x, z) -> visitRegion(x, z, false));
|
||||
Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
|
||||
shutdown();
|
||||
if (!IrisPackBenchmarking.benchmarkInProgress) {
|
||||
if (benchmarking == null) {
|
||||
Iris.info(C.IRIS + "Pregen stopped.");
|
||||
} else {
|
||||
IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory);
|
||||
benchmarking.finishedBenchmark(chunksPerSecondHistory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.parallel.HyperLock;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
@ -70,6 +71,7 @@ class PregenCacheImpl implements PregenCache {
|
||||
return new Plate(key, in);
|
||||
} catch (IOException e){
|
||||
Iris.error("Failed to read pregen cache " + file);
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
return new Plate(key);
|
||||
}
|
||||
@ -82,10 +84,11 @@ class PregenCacheImpl implements PregenCache {
|
||||
hyperLock.lock(plate.pos.x, plate.pos.z);
|
||||
try {
|
||||
File file = fileForPlate(plate.pos);
|
||||
try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
|
||||
plate.write(out);
|
||||
try {
|
||||
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write);
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to write pregen cache " + file);
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
|
@ -32,30 +32,32 @@ import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
||||
private final World world;
|
||||
private final ExecutorService service;
|
||||
private final Executor executor;
|
||||
private final Semaphore semaphore;
|
||||
private final int threads;
|
||||
private final boolean urgent;
|
||||
private final Map<Chunk, Long> lastUse;
|
||||
|
||||
public AsyncPregenMethod(World world, int threads) {
|
||||
public AsyncPregenMethod(World world, int unusedThreads) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
||||
}
|
||||
|
||||
this.world = world;
|
||||
service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||
Executors.newVirtualThreadPerTaskExecutor() :
|
||||
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||
this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
|
||||
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
|
||||
semaphore = new Semaphore(threads);
|
||||
this.semaphore = new Semaphore(this.threads, true);
|
||||
this.urgent = IrisSettings.get().getPregen().useHighPriority;
|
||||
this.lastUse = new KMap<>();
|
||||
}
|
||||
|
||||
@ -67,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
|
||||
Long lastUseTime = lastUse.get(i);
|
||||
if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) {
|
||||
i.unload();
|
||||
lastUse.remove(i);
|
||||
long minTime = M.ms() - 10_000;
|
||||
lastUse.entrySet().removeIf(i -> {
|
||||
final Chunk chunk = i.getKey();
|
||||
final Long lastUseTime = i.getValue();
|
||||
if (!chunk.isLoaded() || lastUseTime == null)
|
||||
return true;
|
||||
if (lastUseTime < minTime) {
|
||||
chunk.unload();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
world.save();
|
||||
}).get();
|
||||
} catch (Throwable e) {
|
||||
@ -81,24 +88,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private void completeChunk(int x, int z, PregenListener listener) {
|
||||
try {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> {
|
||||
lastUse.put(i, M.ms());
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
}).get();
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
unloadAndSaveAllChunks();
|
||||
increaseWorkerThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -110,7 +103,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
public void close() {
|
||||
semaphore.acquireUninterruptibly(threads);
|
||||
unloadAndSaveAllChunks();
|
||||
service.shutdown();
|
||||
executor.shutdown();
|
||||
resetWorkerThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
service.submit(() -> completeChunk(x, z, listener));
|
||||
executor.generate(x, z, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -147,4 +141,100 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void increaseWorkerThreads() {
|
||||
THREAD_COUNT.updateAndGet(i -> {
|
||||
if (i > 0) return 1;
|
||||
var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
|
||||
try {
|
||||
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||
var pool = field.get(null);
|
||||
var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length;
|
||||
if (threads >= adjusted) return 0;
|
||||
|
||||
pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted);
|
||||
return threads;
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted);
|
||||
Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads");
|
||||
if (e instanceof InvocationTargetException) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
public static void resetWorkerThreads() {
|
||||
THREAD_COUNT.updateAndGet(i -> {
|
||||
if (i == 0) return 0;
|
||||
try {
|
||||
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||
var pool = field.get(null);
|
||||
var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class);
|
||||
method.invoke(pool, i);
|
||||
return 0;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("Failed to reset worker threads");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return i;
|
||||
});
|
||||
}
|
||||
|
||||
private interface Executor {
|
||||
void generate(int x, int z, PregenListener listener);
|
||||
default void shutdown() {}
|
||||
}
|
||||
|
||||
private class ServiceExecutor implements Executor {
|
||||
private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||
Executors.newVirtualThreadPerTaskExecutor() :
|
||||
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||
|
||||
public void generate(int x, int z, PregenListener listener) {
|
||||
service.submit(() -> {
|
||||
try {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> {
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
if (i == null) return;
|
||||
lastUse.put(i, M.ms());
|
||||
}).get();
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
service.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private class TicketExecutor implements Executor {
|
||||
@Override
|
||||
public void generate(int x, int z, PregenListener listener) {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
|
||||
.exceptionally(e -> {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
})
|
||||
.thenAccept(i -> {
|
||||
semaphore.release();
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
if (i == null) return;
|
||||
lastUse.put(i, M.ms());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,15 @@ public class IrisSafeguard {
|
||||
Iris.instance.splash();
|
||||
UtilsSFG.splash();
|
||||
}
|
||||
|
||||
public static String mode() {
|
||||
if (unstablemode) {
|
||||
return "unstable";
|
||||
} else if (warningmode) {
|
||||
return "warning";
|
||||
} else {
|
||||
return "stable";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,13 +160,9 @@ public class ServerBootSFG {
|
||||
}
|
||||
|
||||
public static boolean enoughDiskSpace() {
|
||||
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
|
||||
File freeSpace = Bukkit.getWorldContainer();
|
||||
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
|
||||
if (gigabytes > 3){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return gigabytes > 3;
|
||||
}
|
||||
|
||||
private static boolean checkJavac(String path) {
|
||||
|
@ -23,9 +23,11 @@ public class GlobalCacheSVC implements IrisService {
|
||||
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
|
||||
private final KMap<String, PregenCache> globalCache = new KMap<>();
|
||||
private transient boolean lastState;
|
||||
private static boolean disabled = true;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
disabled = false;
|
||||
lastState = !IrisSettings.get().getWorld().isGlobalPregenCache();
|
||||
if (lastState) return;
|
||||
Bukkit.getWorlds().forEach(this::createCache);
|
||||
@ -33,7 +35,8 @@ public class GlobalCacheSVC implements IrisService {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
globalCache.values().forEach(PregenCache::write);
|
||||
disabled = true;
|
||||
globalCache.qclear((world, cache) -> cache.write());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -99,6 +102,7 @@ public class GlobalCacheSVC implements IrisService {
|
||||
}
|
||||
|
||||
private static PregenCache createDefault0(String worldName) {
|
||||
if (disabled) return PregenCache.EMPTY;
|
||||
return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync();
|
||||
}
|
||||
}
|
||||
|
@ -1,317 +1,246 @@
|
||||
package com.volmit.iris.core.service;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.plugin.IrisService;
|
||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import com.volmit.iris.util.scheduling.Looper;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import lombok.Synchronized;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.ServerLoadEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisEngineSVC implements IrisService {
|
||||
public static IrisEngineSVC instance;
|
||||
public boolean isServerShuttingDown = false;
|
||||
public boolean isServerLoaded = false;
|
||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
private ReentrantLock lastUseLock;
|
||||
private KMap<World, Long> lastUse;
|
||||
private List<World> IrisWorlds;
|
||||
private Looper cacheTicker;
|
||||
private Looper trimTicker;
|
||||
private Looper unloadTicker;
|
||||
private final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
private final AtomicInteger tectonicPlates = new AtomicInteger();
|
||||
private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
|
||||
private final AtomicInteger trimmerAlive = new AtomicInteger();
|
||||
private final AtomicInteger unloaderAlive = new AtomicInteger();
|
||||
private final AtomicInteger totalWorlds = new AtomicInteger();
|
||||
private final AtomicDouble maxIdleDuration = new AtomicDouble();
|
||||
private final AtomicDouble minIdleDuration = new AtomicDouble();
|
||||
private final AtomicLong loadedChunks = new AtomicLong();
|
||||
private final KMap<World, Registered> worlds = new KMap<>();
|
||||
private ScheduledExecutorService service;
|
||||
private Looper updateTicker;
|
||||
private PrecisionStopwatch trimAlive;
|
||||
private PrecisionStopwatch unloadAlive;
|
||||
public PrecisionStopwatch trimActiveAlive;
|
||||
public PrecisionStopwatch unloadActiveAlive;
|
||||
private AtomicInteger TotalTectonicPlates;
|
||||
private AtomicInteger TotalQueuedTectonicPlates;
|
||||
private AtomicInteger TotalNotQueuedTectonicPlates;
|
||||
private AtomicBoolean IsUnloadAlive;
|
||||
private AtomicBoolean IsTrimAlive;
|
||||
ChronoLatch cl;
|
||||
|
||||
public List<World> corruptedIrisWorlds = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.cl = new ChronoLatch(5000);
|
||||
lastUse = new KMap<>();
|
||||
lastUseLock = new ReentrantLock();
|
||||
IrisWorlds = new ArrayList<>();
|
||||
IsUnloadAlive = new AtomicBoolean(true);
|
||||
IsTrimAlive = new AtomicBoolean(true);
|
||||
trimActiveAlive = new PrecisionStopwatch();
|
||||
unloadActiveAlive = new PrecisionStopwatch();
|
||||
trimAlive = new PrecisionStopwatch();
|
||||
unloadAlive = new PrecisionStopwatch();
|
||||
TotalTectonicPlates = new AtomicInteger();
|
||||
TotalQueuedTectonicPlates = new AtomicInteger();
|
||||
TotalNotQueuedTectonicPlates = new AtomicInteger();
|
||||
tectonicLimit.set(2);
|
||||
long t = getHardware.getProcessMemory();
|
||||
while (t > 200) {
|
||||
tectonicLimit.getAndAdd(1);
|
||||
t = t - 200;
|
||||
}
|
||||
this.setup();
|
||||
this.TrimLogic();
|
||||
this.UnloadLogic();
|
||||
|
||||
trimAlive.begin();
|
||||
unloadAlive.begin();
|
||||
trimActiveAlive.begin();
|
||||
unloadActiveAlive.begin();
|
||||
|
||||
updateTicker.start();
|
||||
cacheTicker.start();
|
||||
//trimTicker.start();
|
||||
//unloadTicker.start();
|
||||
instance = this;
|
||||
|
||||
var settings = IrisSettings.get().getPerformance();
|
||||
var engine = settings.getEngineSVC();
|
||||
service = Executors.newScheduledThreadPool(0,
|
||||
(engine.isUseVirtualThreads()
|
||||
? Thread.ofVirtual()
|
||||
: Thread.ofPlatform().priority(engine.getPriority()))
|
||||
.name("Iris EngineSVC-", 0)
|
||||
.factory());
|
||||
tectonicLimit.set(settings.getTectonicPlateSize());
|
||||
Bukkit.getWorlds().forEach(this::add);
|
||||
setup();
|
||||
}
|
||||
|
||||
public void engineStatus() {
|
||||
boolean trimAlive = trimTicker.isAlive();
|
||||
boolean unloadAlive = unloadTicker.isAlive();
|
||||
Iris.info("Status:");
|
||||
Iris.info("- Trim: " + trimAlive);
|
||||
Iris.info("- Unload: " + unloadAlive);
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
service.shutdown();
|
||||
updateTicker.interrupt();
|
||||
worlds.keySet().forEach(this::remove);
|
||||
worlds.clear();
|
||||
}
|
||||
|
||||
public static int getTectonicLimit() {
|
||||
return tectonicLimit.get();
|
||||
public void engineStatus(VolmitSender sender) {
|
||||
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
|
||||
sender.sendMessage(C.DARK_PURPLE + "Status:");
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running"));
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped"));
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:");
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Limit: " + C.LIGHT_PURPLE + tectonicLimit.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Total: " + C.LIGHT_PURPLE + tectonicPlates.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2));
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2));
|
||||
sender.sendMessage(C.DARK_PURPLE + "Other:");
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldUnload(WorldUnloadEvent event) {
|
||||
updateWorlds();
|
||||
remove(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(WorldLoadEvent event) {
|
||||
updateWorlds();
|
||||
add(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerBoot(ServerLoadEvent event) {
|
||||
isServerLoaded = true;
|
||||
private void remove(World world) {
|
||||
var entry = worlds.remove(world);
|
||||
if (entry == null) return;
|
||||
entry.close();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPluginDisable(PluginDisableEvent event) {
|
||||
if (event.getPlugin().equals(Iris.instance)) {
|
||||
isServerShuttingDown = true;
|
||||
}
|
||||
private void add(World world) {
|
||||
var access = IrisToolbelt.access(world);
|
||||
if (access == null) return;
|
||||
worlds.put(world, new Registered(world.getName(), access));
|
||||
}
|
||||
|
||||
public void updateWorlds() {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
try {
|
||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
||||
IrisWorlds.add(world);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// no
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
cacheTicker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
long now = System.currentTimeMillis();
|
||||
lastUseLock.lock();
|
||||
try {
|
||||
for (World key : new ArrayList<>(lastUse.keySet())) {
|
||||
Long last = lastUse.get(key);
|
||||
if (last == null)
|
||||
continue;
|
||||
if (now - last > 60000) {
|
||||
lastUse.remove(key);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lastUseLock.unlock();
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
};
|
||||
private synchronized void setup() {
|
||||
if (updateTicker != null && updateTicker.isAlive())
|
||||
return;
|
||||
|
||||
updateTicker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
try {
|
||||
TotalQueuedTectonicPlates.set(0);
|
||||
TotalNotQueuedTectonicPlates.set(0);
|
||||
TotalTectonicPlates.set(0);
|
||||
for (World world : IrisWorlds) {
|
||||
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine();
|
||||
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload());
|
||||
TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions());
|
||||
TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
||||
}
|
||||
if (!isServerShuttingDown && isServerLoaded) {
|
||||
if (!trimTicker.isAlive()) {
|
||||
Iris.info(C.RED + "TrimTicker found dead! Booting it up!");
|
||||
try {
|
||||
TrimLogic();
|
||||
} catch (Exception e) {
|
||||
Iris.error("What happened?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
queuedTectonicPlates.set(0);
|
||||
tectonicPlates.set(0);
|
||||
loadedChunks.set(0);
|
||||
unloaderAlive.set(0);
|
||||
trimmerAlive.set(0);
|
||||
totalWorlds.set(0);
|
||||
|
||||
if (!unloadTicker.isAlive()) {
|
||||
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!");
|
||||
try {
|
||||
UnloadLogic();
|
||||
} catch (Exception e) {
|
||||
Iris.error("What happened?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
double maxDuration = Long.MIN_VALUE;
|
||||
double minDuration = Long.MAX_VALUE;
|
||||
for (var entry : worlds.entrySet()) {
|
||||
var registered = entry.getValue();
|
||||
if (registered.closed) continue;
|
||||
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
totalWorlds.incrementAndGet();
|
||||
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0);
|
||||
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0);
|
||||
|
||||
var engine = registered.getEngine();
|
||||
if (engine == null) continue;
|
||||
|
||||
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
|
||||
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
||||
loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length);
|
||||
|
||||
double duration = engine.getMantle().getAdjustedIdleDuration();
|
||||
if (duration > maxDuration) maxDuration = duration;
|
||||
if (duration < minDuration) minDuration = duration;
|
||||
}
|
||||
maxIdleDuration.set(maxDuration);
|
||||
minIdleDuration.set(minDuration);
|
||||
|
||||
worlds.values().forEach(Registered::update);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
};
|
||||
updateTicker.start();
|
||||
}
|
||||
public void TrimLogic() {
|
||||
if (trimTicker == null || !trimTicker.isAlive()) {
|
||||
trimTicker = new Looper() {
|
||||
private final Supplier<Engine> supplier = createSupplier();
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
trimAlive.reset();
|
||||
private final class Registered {
|
||||
private final String name;
|
||||
private final PlatformChunkGenerator access;
|
||||
private transient ScheduledFuture<?> trimmer;
|
||||
private transient ScheduledFuture<?> unloader;
|
||||
private transient boolean closed;
|
||||
|
||||
private Registered(String name, PlatformChunkGenerator access) {
|
||||
this.name = name;
|
||||
this.access = access;
|
||||
update();
|
||||
}
|
||||
|
||||
private boolean unloaderAlive() {
|
||||
return unloader != null && !unloader.isDone() && !unloader.isCancelled();
|
||||
}
|
||||
|
||||
private boolean trimmerAlive() {
|
||||
return trimmer != null && !trimmer.isDone() && !trimmer.isCancelled();
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private void update() {
|
||||
if (closed || service == null || service.isShutdown())
|
||||
return;
|
||||
|
||||
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
||||
trimmer = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
engine.getMantle().trim(tectonicLimit.get() / lastUse.size());
|
||||
engine.getMantle().trim(tectonicLimit());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||
unloader = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
long unloadStart = System.currentTimeMillis();
|
||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit());
|
||||
if (count > 0) {
|
||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.info(C.RED + "EngineSVC: Failed to trim.");
|
||||
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = lastUse.size();
|
||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
||||
if (time <= 0)
|
||||
return 0;
|
||||
return time;
|
||||
}
|
||||
};
|
||||
trimTicker.start();
|
||||
}
|
||||
}
|
||||
public void UnloadLogic() {
|
||||
if (unloadTicker == null || !unloadTicker.isAlive()) {
|
||||
unloadTicker = new Looper() {
|
||||
private final Supplier<Engine> supplier = createSupplier();
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
unloadAlive.reset();
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
long unloadStart = System.currentTimeMillis();
|
||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size());
|
||||
if (count > 0) {
|
||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.info(C.RED + "EngineSVC: Failed to unload.");
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = lastUse.size();
|
||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
||||
if (time <= 0)
|
||||
return 0;
|
||||
return time;
|
||||
}
|
||||
};
|
||||
unloadTicker.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Supplier<Engine> createSupplier() {
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
return () -> {
|
||||
List<World> worlds = Bukkit.getWorlds();
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
try {
|
||||
for (int j = 0; j < worlds.size(); j++) {
|
||||
World world = worlds.get(i.getAndIncrement());
|
||||
PlatformChunkGenerator generator = IrisToolbelt.access(world);
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (generator != null) {
|
||||
Engine engine = generator.getEngine();
|
||||
boolean closed = engine.getMantle().getData().isClosed();
|
||||
if (engine != null && !engine.isStudio() && !closed) {
|
||||
lastUseLock.lock();
|
||||
lastUse.put(world, System.currentTimeMillis());
|
||||
lastUseLock.unlock();
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
private int tectonicLimit() {
|
||||
return tectonicLimit.get() / Math.max(worlds.size(), 1);
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private void close() {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
|
||||
if (trimmer != null) {
|
||||
trimmer.cancel(false);
|
||||
trimmer = null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
cacheTicker.interrupt();
|
||||
trimTicker.interrupt();
|
||||
unloadTicker.interrupt();
|
||||
lastUse.clear();
|
||||
if (unloader != null) {
|
||||
unloader.cancel(false);
|
||||
unloader = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Engine getEngine() {
|
||||
if (closed) return null;
|
||||
return access.getEngine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import org.bukkit.util.Vector;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@ -80,6 +81,8 @@ public class WandSVC implements IrisService {
|
||||
|
||||
try {
|
||||
Location[] f = getCuboid(p);
|
||||
if (f == null || f[0] == null || f[1] == null)
|
||||
return null;
|
||||
Cuboid c = new Cuboid(f[0], f[1]);
|
||||
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
|
||||
|
||||
@ -198,7 +201,9 @@ public class WandSVC implements IrisService {
|
||||
public static Location stringToLocation(String s) {
|
||||
try {
|
||||
String[] f = s.split("\\Q in \\E");
|
||||
if (f.length != 2) return null;
|
||||
String[] g = f[0].split("\\Q,\\E");
|
||||
if (g.length != 3) return null;
|
||||
return new Location(Bukkit.getWorld(f[1]), Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2]));
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
@ -357,6 +362,7 @@ public class WandSVC implements IrisService {
|
||||
try {
|
||||
if ((IrisSettings.get().getWorld().worldEditWandCUI && isHoldingWand(p)) || isWand(p.getInventory().getItemInMainHand())) {
|
||||
Location[] d = getCuboid(p);
|
||||
if (d == null || d[0] == null || d[1] == null) return;
|
||||
new WandSelection(new Cuboid(d[0], d[1]), p).draw();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
@ -9,51 +9,43 @@ import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.exceptions.IrisException;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class IrisPackBenchmarking {
|
||||
@Getter
|
||||
public static IrisPackBenchmarking instance;
|
||||
public static boolean benchmarkInProgress = false;
|
||||
private static final ThreadLocal<IrisPackBenchmarking> instance = new ThreadLocal<>();
|
||||
private final PrecisionStopwatch stopwatch = new PrecisionStopwatch();
|
||||
private final IrisDimension dimension;
|
||||
private final int radius;
|
||||
private final boolean gui;
|
||||
|
||||
public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) {
|
||||
instance = this;
|
||||
this.dimension = dimension;
|
||||
this.radius = radius;
|
||||
this.gui = gui;
|
||||
runBenchmark();
|
||||
}
|
||||
|
||||
public static IrisPackBenchmarking getInstance() {
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
private void runBenchmark() {
|
||||
Thread.ofVirtual()
|
||||
.name("PackBenchmarking")
|
||||
.start(() -> {
|
||||
Iris.info("Setting up benchmark environment ");
|
||||
benchmarkInProgress = true;
|
||||
File file = new File("benchmark");
|
||||
if (file.exists()) {
|
||||
deleteDirectory(file.toPath());
|
||||
}
|
||||
IO.delete(new File(Bukkit.getWorldContainer(), "benchmark"));
|
||||
createBenchmark();
|
||||
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
||||
J.sleep(1000);
|
||||
@ -66,13 +58,9 @@ public class IrisPackBenchmarking {
|
||||
|
||||
}
|
||||
|
||||
public boolean getBenchmarkInProgress() {
|
||||
return benchmarkInProgress;
|
||||
}
|
||||
|
||||
public void finishedBenchmark(KList<Integer> cps) {
|
||||
try {
|
||||
String time = Form.duration(stopwatch.getMillis());
|
||||
String time = Form.duration((long) stopwatch.getMilliseconds());
|
||||
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
|
||||
Iris.info("-----------------");
|
||||
Iris.info("Results:");
|
||||
@ -83,11 +71,7 @@ public class IrisPackBenchmarking {
|
||||
Iris.info(" - Lowest CPS: " + findLowest(cps));
|
||||
Iris.info("-----------------");
|
||||
Iris.info("Creating a report..");
|
||||
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks");
|
||||
profilers.mkdir();
|
||||
|
||||
File results = new File(profilers, dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||
results.getParentFile().mkdirs();
|
||||
File results = Iris.instance.getDataFile("packbenchmarks", dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||
KMap<String, Double> metrics = engine.getMetrics().pull();
|
||||
try (FileWriter writer = new FileWriter(results)) {
|
||||
writer.write("-----------------\n");
|
||||
@ -143,13 +127,18 @@ public class IrisPackBenchmarking {
|
||||
}
|
||||
|
||||
private void startBenchmark() {
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
.builder()
|
||||
.gui(gui)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), Bukkit.getWorld("benchmark")
|
||||
);
|
||||
try {
|
||||
instance.set(this);
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
.builder()
|
||||
.gui(gui)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), Bukkit.getWorld("benchmark")
|
||||
);
|
||||
} finally {
|
||||
instance.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateAverage(KList<Integer> list) {
|
||||
@ -178,26 +167,4 @@ public class IrisPackBenchmarking {
|
||||
private int findHighest(KList<Integer> list) {
|
||||
return Collections.max(list);
|
||||
}
|
||||
|
||||
private boolean deleteDirectory(Path dir) {
|
||||
try {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -142,7 +142,7 @@ public class IrisToolbelt {
|
||||
* @return the pregenerator job (already started)
|
||||
*/
|
||||
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||
return pregenerate(task, method, engine, true);
|
||||
return pregenerate(task, method, engine, IrisSettings.get().getPregen().useCacheByDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,7 +96,6 @@ public class IrisEngine implements Engine {
|
||||
private ExecutionEnvironment.Engine execution;
|
||||
private EngineWorldManager worldManager;
|
||||
private volatile int parallelism;
|
||||
private volatile int minHeight;
|
||||
private boolean failing;
|
||||
private boolean closed;
|
||||
private int cacheId;
|
||||
@ -129,7 +128,6 @@ public class IrisEngine implements Engine {
|
||||
getData().setEngine(this);
|
||||
getData().loadPrefetch(this);
|
||||
Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
|
||||
minHeight = 0;
|
||||
failing = false;
|
||||
closed = false;
|
||||
art = J.ar(this::tickRandomPlayer, 0);
|
||||
@ -180,7 +178,10 @@ public class IrisEngine implements Engine {
|
||||
File[] roots = getData().getLoaders()
|
||||
.values()
|
||||
.stream()
|
||||
.map(ResourceLoader::getRoot)
|
||||
.map(ResourceLoader::getFolderName)
|
||||
.map(n -> new File(getData().getDataFolder(), n))
|
||||
.filter(File::exists)
|
||||
.filter(File::isDirectory)
|
||||
.toArray(File[]::new);
|
||||
hash32.complete(IO.hashRecursive(roots));
|
||||
});
|
||||
@ -472,7 +473,7 @@ public class IrisEngine implements Engine {
|
||||
getEngineData().getStatistics().generatedChunk();
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
Hunk<BlockData> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y + getMinHeight(), z + zz, t));
|
||||
Hunk<BlockData> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
|
||||
|
||||
if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
@ -55,6 +55,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -367,7 +368,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
private void spawn(IrisPosition pos, IrisEntitySpawn i) {
|
||||
IrisSpawner ref = i.getReferenceSpawner();
|
||||
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ()))
|
||||
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4))
|
||||
return;
|
||||
|
||||
int s = i.spawn(getEngine(), pos, RNG.r);
|
||||
@ -422,9 +423,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
energy += 0.3;
|
||||
fixEnergy();
|
||||
getEngine().cleanupMantleChunk(e.getX(), e.getZ());
|
||||
var ref = new WeakReference<>(e.getWorld());
|
||||
int x = e.getX(), z = e.getZ();
|
||||
J.s(() -> {
|
||||
World world = ref.get();
|
||||
if (world == null || !world.isChunkLoaded(x, z))
|
||||
return;
|
||||
energy += 0.3;
|
||||
fixEnergy();
|
||||
getEngine().cleanupMantleChunk(x, z);
|
||||
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
|
||||
|
||||
if (generated) {
|
||||
//INMS.get().injectBiomesFromMantle(e, getMantle());
|
||||
|
@ -78,7 +78,6 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -141,7 +140,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return getTarget().getWorld().minHeight();
|
||||
}
|
||||
|
||||
void setMinHeight(int min);
|
||||
default void setMinHeight(int min) {
|
||||
getTarget().getWorld().minHeight(min);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException {
|
||||
@ -288,76 +289,79 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return;
|
||||
}
|
||||
|
||||
var chunk = mantle.getChunk(c);
|
||||
if (chunk.isFlagged(MantleFlag.ETCHED)) return;
|
||||
chunk.flag(MantleFlag.ETCHED, true);
|
||||
var chunk = mantle.getChunk(c).use();
|
||||
try {
|
||||
Semaphore semaphore = new Semaphore(3);
|
||||
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
||||
int betterY = y + getWorld().minHeight();
|
||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
||||
});
|
||||
})));
|
||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||
});
|
||||
})));
|
||||
|
||||
Semaphore semaphore = new Semaphore(3);
|
||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
||||
int betterY = y + getWorld().minHeight();
|
||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
||||
});
|
||||
})));
|
||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||
});
|
||||
})));
|
||||
|
||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
KMap<Long, Integer> updates = new KMap<>();
|
||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
||||
return;
|
||||
}
|
||||
boolean u = false;
|
||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
||||
u = true;
|
||||
}
|
||||
|
||||
if (u) {
|
||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
||||
if (vv != null) {
|
||||
return Math.max(vv, y);
|
||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
KMap<Long, Integer> updates = new KMap<>();
|
||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
||||
return;
|
||||
}
|
||||
boolean u = false;
|
||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
||||
u = true;
|
||||
}
|
||||
|
||||
return y;
|
||||
if (u) {
|
||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
||||
if (vv != null) {
|
||||
return Math.max(vv, y);
|
||||
}
|
||||
|
||||
return y;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (v != null && v.isUpdate()) {
|
||||
int vx = x & 15;
|
||||
int vz = z & 15;
|
||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||
updateLighting(x, y, z, c);
|
||||
}
|
||||
}
|
||||
});
|
||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||
}, RNG.r.i(0, 20))));
|
||||
});
|
||||
|
||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (v != null && v.isUpdate()) {
|
||||
int vx = x & 15;
|
||||
int vz = z & 15;
|
||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||
updateLighting(x, y, z, c);
|
||||
}
|
||||
}
|
||||
});
|
||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||
}, RNG.r.i(0, 20))));
|
||||
|
||||
try {
|
||||
semaphore.acquire(3);
|
||||
} catch (InterruptedException ignored) {}
|
||||
try {
|
||||
semaphore.acquire(3);
|
||||
} catch (InterruptedException ignored) {}
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable run(Semaphore semaphore, Runnable runnable) {
|
||||
@ -454,14 +458,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
Arrays.parallelSort(nitems, (a, b) -> rng.nextInt());
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
}
|
||||
for (int i = nitems.length; i > 1; i--) {
|
||||
int j = rng.nextInt(i);
|
||||
ItemStack tmp = nitems[i - 1];
|
||||
nitems[i - 1] = nitems[j];
|
||||
nitems[j] = tmp;
|
||||
}
|
||||
|
||||
inventory.setContents(nitems);
|
||||
|
@ -47,9 +47,7 @@ public class EnginePlayer {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
sample();
|
||||
|
||||
if (!IrisSettings.get().getWorld().isEffectSystem())
|
||||
if (sample() || !IrisSettings.get().getWorld().isEffectSystem())
|
||||
return;
|
||||
|
||||
J.a(() -> {
|
||||
@ -81,22 +79,22 @@ public class EnginePlayer {
|
||||
return M.ms() - lastSample;
|
||||
}
|
||||
|
||||
public void sample() {
|
||||
public boolean sample() {
|
||||
Location current = player.getLocation().clone();
|
||||
if (current.getWorld() != engine.getWorld().realWorld())
|
||||
return true;
|
||||
try {
|
||||
if (ticksSinceLastSample() > 55 && player.getLocation().distanceSquared(lastLocation) > 9 * 9) {
|
||||
lastLocation = player.getLocation().clone();
|
||||
if (ticksSinceLastSample() > 55 && current.distanceSquared(lastLocation) > 9 * 9) {
|
||||
lastLocation = current;
|
||||
lastSample = M.ms();
|
||||
sampleBiomeRegion();
|
||||
biome = engine.getBiome(current);
|
||||
region = engine.getRegion(current);
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void sampleBiomeRegion() {
|
||||
Location l = player.getLocation();
|
||||
biome = engine.getBiome(l);
|
||||
region = engine.getRegion(l);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -289,23 +289,25 @@ public interface EngineMantle extends IObjectPlacer {
|
||||
}
|
||||
|
||||
default void cleanupChunk(int x, int z) {
|
||||
if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) {
|
||||
getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> {
|
||||
getMantle().deleteChunkSlice(x, z, BlockData.class);
|
||||
getMantle().deleteChunkSlice(x, z, String.class);
|
||||
getMantle().deleteChunkSlice(x, z, MatterCavern.class);
|
||||
getMantle().deleteChunkSlice(x, z, MatterFluidBody.class);
|
||||
if (!isCovered(x, z)) return;
|
||||
MantleChunk chunk = getMantle().getChunk(x, z).use();
|
||||
try {
|
||||
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
|
||||
chunk.deleteSlices(BlockData.class);
|
||||
chunk.deleteSlices(String.class);
|
||||
chunk.deleteSlices(MatterCavern.class);
|
||||
chunk.deleteSlices(MatterFluidBody.class);
|
||||
});
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
default long getToUnload(){
|
||||
return getMantle().getToUnload().size();
|
||||
default long getUnloadRegionCount() {
|
||||
return getMantle().getUnloadRegionCount();
|
||||
}
|
||||
default long getNotQueuedLoadedRegions(){
|
||||
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size();
|
||||
}
|
||||
default double getTectonicDuration(){
|
||||
return getMantle().getAdjustedIdleDuration().get();
|
||||
|
||||
default double getAdjustedIdleDuration() {
|
||||
return getMantle().getAdjustedIdleDuration();
|
||||
}
|
||||
}
|
@ -60,8 +60,9 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
|
||||
for (int i = -radius; i <= radius; i++) {
|
||||
for (int j = -radius; j <= radius; j++) {
|
||||
int r = radius / 4;
|
||||
for (int i = -r; i <= r; i++) {
|
||||
for (int j = -r; j <= r; j++) {
|
||||
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
|
||||
}
|
||||
}
|
||||
@ -143,7 +144,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
if (cx >= this.x - radius && cx <= this.x + radius
|
||||
&& cz >= this.z - radius && cz <= this.z + radius) {
|
||||
MantleChunk chunk = cachedChunks.get(Cache.key(cx, cz));
|
||||
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
|
||||
|
||||
if (chunk == null) {
|
||||
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
|
||||
@ -152,6 +153,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
Matter matter = chunk.getOrCreate(y >> 4);
|
||||
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
|
||||
} else {
|
||||
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,9 +642,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
cachedChunks.values().removeIf(c -> {
|
||||
c.release();
|
||||
return true;
|
||||
});
|
||||
var iterator = cachedChunks.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next().release();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,21 +58,21 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
|
||||
@ChunkCoordinates
|
||||
private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) {
|
||||
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4);
|
||||
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
|
||||
}
|
||||
|
||||
private int computeRadius() {
|
||||
var dimension = getDimension();
|
||||
int max = 0;
|
||||
|
||||
max = Math.max(max, dimension.getCarving().getMaxRange(getData()));
|
||||
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
|
||||
|
||||
for (var i : dimension.getAllRegions(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData()));
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
}
|
||||
|
||||
for (var i : dimension.getAllBiomes(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData()));
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
}
|
||||
|
||||
return max;
|
||||
|
@ -43,7 +43,6 @@ import org.bukkit.block.data.BlockData;
|
||||
public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||
private final RNG rng;
|
||||
private final BlockData AIR = Material.CAVE_AIR.createBlockData();
|
||||
private final BlockData WATER = Material.WATER.createBlockData();
|
||||
private final BlockData LAVA = Material.LAVA.createBlockData();
|
||||
private final IrisDecorantActuator decorant;
|
||||
|
||||
@ -103,7 +102,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||
}
|
||||
|
||||
if (c.isWater()) {
|
||||
output.set(rx, yy, rz, WATER);
|
||||
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
||||
} else if (c.isLava()) {
|
||||
output.set(rx, yy, rz, LAVA);
|
||||
} else {
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.volmit.iris.engine.object;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.loader.IrisRegistrant;
|
||||
import com.volmit.iris.core.nms.INMS;
|
||||
@ -34,8 +35,8 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ -202,6 +203,14 @@ public class IrisBlockData extends IrisRegistrant {
|
||||
public TileData tryGetTile(IrisData data) {
|
||||
//TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities.
|
||||
var type = getBlockData(data).getMaterial();
|
||||
if (type == Material.SPAWNER && this.data.containsKey("entitySpawn")) {
|
||||
String id = (String) this.data.get("entitySpawn");
|
||||
if (tileData == null) tileData = new KMap<>();
|
||||
KMap<String, Object> spawnData = (KMap<String, Object>) tileData.computeIfAbsent("SpawnData", k -> new KMap<>());
|
||||
KMap<String, Object> entity = (KMap<String, Object>) spawnData.computeIfAbsent("entity", k -> new KMap<>());
|
||||
entity.putIfAbsent("id", Identifier.fromString(id).toString());
|
||||
}
|
||||
|
||||
if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty())
|
||||
return null;
|
||||
return new TileData(type, this.tileData);
|
||||
|
@ -61,21 +61,25 @@ public class IrisCarving {
|
||||
|
||||
|
||||
@BlockCoordinates
|
||||
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||
doCarving(writer, rng, engine, x, y, z, -1);
|
||||
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
|
||||
doCarving(writer, rng, engine, x, y, z, depth, -1);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
|
||||
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
int nextRecursion = recursion + 1;
|
||||
|
||||
if (caves.isNotEmpty()) {
|
||||
for (IrisCavePlacer i : caves) {
|
||||
i.generateCave(writer, rng, engine, x, y, z, waterHint);
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint);
|
||||
}
|
||||
}
|
||||
|
||||
if (ravines.isNotEmpty()) {
|
||||
for (IrisRavinePlacer i : ravines) {
|
||||
i.generateRavine(writer, rng, engine, x, y, z, waterHint);
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,15 +108,18 @@ public class IrisCarving {
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxRange(IrisData data) {
|
||||
public int getMaxRange(IrisData data, int recursion) {
|
||||
int max = 0;
|
||||
int nextRecursion = recursion + 1;
|
||||
|
||||
for (IrisCavePlacer i : caves) {
|
||||
max = Math.max(max, i.getSize(data));
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
max = Math.max(max, i.getSize(data, nextRecursion));
|
||||
}
|
||||
|
||||
for (IrisRavinePlacer i : ravines) {
|
||||
max = Math.max(max, i.getSize(data));
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
max = Math.max(max, i.getSize(data, nextRecursion));
|
||||
}
|
||||
|
||||
if (elipsoids.isNotEmpty()) {
|
||||
|
@ -66,10 +66,10 @@ public class IrisCave extends IrisRegistrant {
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generate(writer, rng, engine, x, y, z, -1);
|
||||
generate(writer, rng, engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
|
||||
double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
|
||||
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> {
|
||||
@ -92,7 +92,7 @@ public class IrisCave extends IrisRegistrant {
|
||||
int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight());
|
||||
|
||||
for (IrisPosition i : points) {
|
||||
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), h);
|
||||
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
|
||||
}
|
||||
|
||||
MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
|
||||
@ -108,7 +108,7 @@ public class IrisCave extends IrisRegistrant {
|
||||
|
||||
}
|
||||
|
||||
public int getMaxSize(IrisData data) {
|
||||
return getWorm().getMaxDistance() + fork.getMaxRange(data);
|
||||
public int getMaxSize(IrisData data, int depth) {
|
||||
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,10 @@ public class IrisCavePlacer implements IRare {
|
||||
@Desc("The cave to place")
|
||||
@RegistryListResource(IrisCave.class)
|
||||
private String cave;
|
||||
@MinNumber(1)
|
||||
@MaxNumber(256)
|
||||
@Desc("The maximum recursion depth")
|
||||
private int maxRecursion = 16;
|
||||
@Desc("If set to true, this cave is allowed to break the surface")
|
||||
private boolean breakSurface = true;
|
||||
@Desc("The height range this cave can spawn at. If breakSurface is false, the output of this range will be clamped by the current world height to prevent surface breaking.")
|
||||
@ -60,10 +64,10 @@ public class IrisCavePlacer implements IRare {
|
||||
}
|
||||
|
||||
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generateCave(mantle, rng, engine, x, y, z, -1);
|
||||
generateCave(mantle, rng, engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
|
||||
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
if (fail.get()) {
|
||||
return;
|
||||
}
|
||||
@ -92,18 +96,18 @@ public class IrisCavePlacer implements IRare {
|
||||
}
|
||||
|
||||
try {
|
||||
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), waterHint);
|
||||
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
fail.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize(IrisData data) {
|
||||
public int getSize(IrisData data, int depth) {
|
||||
IrisCave cave = getRealCave(data);
|
||||
|
||||
if (cave != null) {
|
||||
return cave.getMaxSize(data);
|
||||
return cave.getMaxSize(data, depth);
|
||||
}
|
||||
|
||||
return 32;
|
||||
|
@ -28,7 +28,6 @@ import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
import com.volmit.iris.util.matter.MatterMarker;
|
||||
import com.volmit.iris.util.matter.slices.MarkerMatter;
|
||||
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -38,9 +37,6 @@ import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
||||
@Snippet("entity-spawn")
|
||||
@Accessors(chain = true)
|
||||
@ -116,8 +112,8 @@ public class IrisEntitySpawn implements IRare {
|
||||
World world = gen.getWorld().realWorld();
|
||||
if (spawns > 0) {
|
||||
|
||||
if (referenceMarker != null) {
|
||||
gen.getMantle().getMantle().remove(c.getX(), c.getY(), c.getZ(), MatterMarker.class);
|
||||
if (referenceMarker != null && referenceMarker.shouldExhaust()) {
|
||||
gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class);
|
||||
}
|
||||
|
||||
for (int id = 0; id < spawns; id++) {
|
||||
|
@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant {
|
||||
private boolean emptyAbove = true;
|
||||
|
||||
@Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.")
|
||||
private double exhaustionChance = 0.33;
|
||||
private double exhaustionChance = 0;
|
||||
|
||||
public boolean shouldExhaust() {
|
||||
return RNG.r.chance(exhaustionChance);
|
||||
return exhaustionChance > RNG.r.nextDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,10 +93,10 @@ public class IrisRavine extends IrisRegistrant {
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generate(writer, rng, engine, x, y, z, -1);
|
||||
generate(writer, rng, engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> {
|
||||
});
|
||||
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData());
|
||||
@ -135,7 +135,7 @@ public class IrisRavine extends IrisRegistrant {
|
||||
int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ()));
|
||||
int surface = (int) Math.round(rsurface - depth * 0.45);
|
||||
|
||||
fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), Math.max(highestWater, waterHint));
|
||||
fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, Math.max(highestWater, waterHint));
|
||||
|
||||
for (int i = surface + depth; i >= surface; i--) {
|
||||
if (i % ribThickness == 0) {
|
||||
@ -184,7 +184,7 @@ public class IrisRavine extends IrisRegistrant {
|
||||
|
||||
}
|
||||
|
||||
public int getMaxSize(IrisData data) {
|
||||
return getWorm().getMaxDistance() + fork.getMaxRange(data);
|
||||
public int getMaxSize(IrisData data, int depth) {
|
||||
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
|
||||
}
|
||||
}
|
||||
|
@ -50,16 +50,20 @@ public class IrisRavinePlacer implements IRare {
|
||||
@Desc("The ravine to place")
|
||||
@RegistryListResource(IrisRavine.class)
|
||||
private String ravine;
|
||||
@MinNumber(1)
|
||||
@MaxNumber(256)
|
||||
@Desc("The maximum recursion depth")
|
||||
private int maxRecursion = 100;
|
||||
|
||||
public IrisRavine getRealRavine(IrisData data) {
|
||||
return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine()));
|
||||
}
|
||||
|
||||
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generateRavine(mantle, rng, engine, x, y, z, -1);
|
||||
generateRavine(mantle, rng, engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
|
||||
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
if (fail.get()) {
|
||||
return;
|
||||
}
|
||||
@ -80,14 +84,14 @@ public class IrisRavinePlacer implements IRare {
|
||||
try {
|
||||
int xx = x + rng.nextInt(15);
|
||||
int zz = z + rng.nextInt(15);
|
||||
ravine.generate(mantle, rng, engine, xx, y, zz, waterHint);
|
||||
ravine.generate(mantle, rng, engine, xx, y, zz, recursion, waterHint);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
fail.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize(IrisData data) {
|
||||
return getRealRavine(data).getMaxSize(data);
|
||||
public int getSize(IrisData data, int depth) {
|
||||
return getRealRavine(data).getMaxSize(data, depth);
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import com.volmit.iris.util.io.ReactiveFolder;
|
||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.Looper;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Setter;
|
||||
@ -86,12 +87,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
private final boolean studio;
|
||||
private final AtomicInteger a = new AtomicInteger(0);
|
||||
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
||||
private Engine engine;
|
||||
private Looper hotloader;
|
||||
private StudioMode lastMode;
|
||||
private DummyBiomeProvider dummyBiomeProvider;
|
||||
private volatile Engine engine;
|
||||
private volatile Looper hotloader;
|
||||
private volatile StudioMode lastMode;
|
||||
private volatile DummyBiomeProvider dummyBiomeProvider;
|
||||
@Setter
|
||||
private StudioGenerator studioGenerator;
|
||||
private volatile StudioGenerator studioGenerator;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
@ -110,20 +111,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
|
||||
}
|
||||
|
||||
private static Field getField(Class clazz, String fieldName)
|
||||
throws NoSuchFieldException {
|
||||
try {
|
||||
return clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Class superClass = clazz.getSuperclass();
|
||||
if (superClass == null) {
|
||||
throw e;
|
||||
} else {
|
||||
return getField(superClass, fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldInit(WorldInitEvent event) {
|
||||
try {
|
||||
@ -158,6 +145,20 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) {
|
||||
Location location = new Location(world, 0, 64, 0);
|
||||
PaperLib.getChunkAtAsync(location)
|
||||
.thenAccept(c -> {
|
||||
World w = c.getWorld();
|
||||
if (!w.getSpawnLocation().equals(location))
|
||||
return;
|
||||
w.setSpawnLocation(location.add(0, w.getHighestBlockYAt(location) - 64, 0));
|
||||
});
|
||||
return location;
|
||||
}
|
||||
|
||||
private void setupEngine() {
|
||||
IrisData data = IrisData.get(dataLocation);
|
||||
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
|
||||
@ -301,7 +302,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
hotloader.interrupt();
|
||||
}
|
||||
|
||||
getEngine().close();
|
||||
final Engine engine = getEngine();
|
||||
if (engine != null && !engine.isClosed())
|
||||
engine.close();
|
||||
folder.clear();
|
||||
populators.clear();
|
||||
|
||||
|
@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils;
|
||||
*/
|
||||
public class AtomicAverage {
|
||||
protected final AtomicDoubleArray values;
|
||||
protected int cursor;
|
||||
private double average;
|
||||
private double lastSum;
|
||||
private boolean dirty;
|
||||
private boolean brandNew;
|
||||
protected transient int cursor;
|
||||
private transient double average;
|
||||
private transient double lastSum;
|
||||
private transient boolean dirty;
|
||||
private transient boolean brandNew;
|
||||
|
||||
/**
|
||||
* Create an average holder
|
||||
@ -57,7 +57,7 @@ public class AtomicAverage {
|
||||
*
|
||||
* @param i the value
|
||||
*/
|
||||
public void put(double i) {
|
||||
public synchronized void put(double i) {
|
||||
|
||||
try {
|
||||
dirty = true;
|
||||
|
@ -23,11 +23,9 @@ import com.volmit.iris.util.function.Consumer2;
|
||||
import com.volmit.iris.util.function.Consumer3;
|
||||
import com.volmit.iris.util.scheduling.Queue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
@ -373,6 +371,20 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
return g;
|
||||
}
|
||||
|
||||
public KMap<K, V> qclear(BiConsumer<K, V> action) {
|
||||
final Iterator<Map.Entry<K, V>> it = entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
final Map.Entry<K, V> entry = it.next();
|
||||
it.remove();
|
||||
try {
|
||||
action.accept(entry.getKey(), entry.getValue());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a keypair queue
|
||||
*
|
||||
|
@ -18,29 +18,67 @@
|
||||
|
||||
package com.volmit.iris.util.collection;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class KSet<T> extends HashSet<T> {
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class KSet<T> extends AbstractSet<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ConcurrentHashMap<T, Boolean> map;
|
||||
|
||||
public KSet() {
|
||||
super();
|
||||
map = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public KSet(Collection<? extends T> c) {
|
||||
super(c);
|
||||
this();
|
||||
addAll(c);
|
||||
}
|
||||
|
||||
public KSet(int initialCapacity, float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
public KSet(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
map = new ConcurrentHashMap<>(initialCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
return map.putIfAbsent(t, Boolean.TRUE) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return map.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
public KSet<T> copy() {
|
||||
return new KSet<T>(this);
|
||||
return new KSet<>(this);
|
||||
}
|
||||
}
|
||||
|
@ -87,4 +87,21 @@ public class IrisContext {
|
||||
public IrisComplex getComplex() {
|
||||
return engine.getComplex();
|
||||
}
|
||||
|
||||
public KMap<String, Object> asContext() {
|
||||
var hash32 = engine.getHash32().getNow(null);
|
||||
var dimension = engine.getDimension();
|
||||
var mantle = engine.getMantle();
|
||||
return new KMap<String, Object>()
|
||||
.qput("studio", engine.isStudio())
|
||||
.qput("closed", engine.isClosed())
|
||||
.qput("pack", new KMap<>()
|
||||
.qput("key", dimension == null ? "" : dimension.getLoadKey())
|
||||
.qput("version", dimension == null ? "" : dimension.getVersion())
|
||||
.qput("hash", hash32 == null ? "" : Long.toHexString(hash32)))
|
||||
.qput("mantle", new KMap<>()
|
||||
.qput("idle", mantle.getAdjustedIdleDuration())
|
||||
.qput("loaded", mantle.getLoadedRegionCount())
|
||||
.qput("queued", mantle.getUnloadRegionCount()));
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,8 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter {
|
||||
}
|
||||
|
||||
J.aBukkit(() -> {
|
||||
if (!call(new VolmitSender(sender), args)) {
|
||||
var volmit = new VolmitSender(sender);
|
||||
if (!call(volmit, args)) {
|
||||
|
||||
if (IrisSettings.get().getGeneral().isCommandSounds()) {
|
||||
if (sender instanceof Player) {
|
||||
@ -169,7 +170,7 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(C.RED + "Unknown Iris Command");
|
||||
volmit.sendMessage(C.RED + "Unknown Iris Command");
|
||||
} else {
|
||||
if (IrisSettings.get().getGeneral().isCommandSounds()) {
|
||||
if (sender instanceof Player) {
|
||||
|
@ -376,6 +376,28 @@ public enum C {
|
||||
return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2);
|
||||
}
|
||||
|
||||
public static String mini(String s) {
|
||||
String msg = compress(s);
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean c = false;
|
||||
|
||||
for (char i : msg.toCharArray()) {
|
||||
if (!c) {
|
||||
if (i == C.COLOR_CHAR) {
|
||||
c = true;
|
||||
continue;
|
||||
}
|
||||
b.append(i);
|
||||
} else {
|
||||
c = false;
|
||||
C o = C.getByChar(i);
|
||||
b.append(o.token);
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static String aura(String s, int hrad, int srad, int vrad) {
|
||||
return aura(s, hrad, srad, vrad, 0.3D);
|
||||
}
|
||||
|
@ -18,8 +18,14 @@
|
||||
|
||||
package com.volmit.iris.util.io;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import org.apache.commons.io.function.IOConsumer;
|
||||
import org.apache.commons.io.function.IOFunction;
|
||||
import lombok.SneakyThrows;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
@ -29,6 +35,8 @@ import org.dom4j.io.XMLWriter;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@ -140,8 +148,7 @@ public class IO {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (var fin = new FileInputStream(file)) {
|
||||
var din = new CheckedInputStream(fin, crc);
|
||||
try (var din = new CheckedInputStream(readDeterministic(file), crc)) {
|
||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
@ -158,10 +165,43 @@ public class IO {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static InputStream readDeterministic(File file) throws IOException {
|
||||
if (!file.getName().endsWith(".json"))
|
||||
return new FileInputStream(file);
|
||||
|
||||
JsonElement json;
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
json = JsonParser.parseReader(reader);
|
||||
}
|
||||
|
||||
var queue = new LinkedList<JsonElement>();
|
||||
queue.add(json);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var element = queue.pop();
|
||||
Collection<JsonElement> add = List.of();
|
||||
|
||||
if (element instanceof JsonObject obj) {
|
||||
var map = obj.asMap();
|
||||
var sorted = new TreeMap<>(map);
|
||||
map.clear();
|
||||
map.putAll(sorted);
|
||||
|
||||
add = sorted.values();
|
||||
} else if (element instanceof JsonArray array) {
|
||||
add = array.asList();
|
||||
}
|
||||
|
||||
add.stream().filter(e -> e.isJsonObject() || e.isJsonArray()).forEach(queue::add);
|
||||
}
|
||||
|
||||
return toInputStream(json.toString());
|
||||
}
|
||||
|
||||
public static String hash(File b) {
|
||||
try {
|
||||
MessageDigest d = MessageDigest.getInstance("SHA-256");
|
||||
DigestInputStream din = new DigestInputStream(new FileInputStream(b), d);
|
||||
DigestInputStream din = new DigestInputStream(readDeterministic(b), d);
|
||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||
din.close();
|
||||
return bytesToHex(din.getMessageDigest().digest());
|
||||
@ -1626,4 +1666,19 @@ public class IO {
|
||||
.addAttribute("version", "4");
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static <T extends OutputStream> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
|
||||
File dir = new File(file.getParentFile(), ".tmp");
|
||||
dir.mkdirs();
|
||||
dir.deleteOnExit();
|
||||
File temp = File.createTempFile("iris",".bin", dir);
|
||||
try {
|
||||
try (var out = builder.apply(new FileOutputStream(temp))) {
|
||||
action.accept(out);
|
||||
}
|
||||
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
} finally {
|
||||
temp.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,16 +31,22 @@ public class JarScanner {
|
||||
private final KSet<Class<?>> classes;
|
||||
private final File jar;
|
||||
private final String superPackage;
|
||||
private final boolean report;
|
||||
|
||||
/**
|
||||
* Create a scanner
|
||||
*
|
||||
* @param jar the path to the jar
|
||||
*/
|
||||
public JarScanner(File jar, String superPackage) {
|
||||
public JarScanner(File jar, String superPackage, boolean report) {
|
||||
this.jar = jar;
|
||||
this.classes = new KSet<>();
|
||||
this.superPackage = superPackage;
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public JarScanner(File jar, String superPackage) {
|
||||
this(jar, superPackage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,7 +71,8 @@ public class JarScanner {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(c);
|
||||
classes.add(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
} catch (Throwable e) {
|
||||
if (!report) continue;
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -21,20 +21,20 @@ package com.volmit.iris.util.mantle;
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.mantle.EngineMantle;
|
||||
import com.volmit.iris.engine.mantle.MantleWriter;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.matter.Matter;
|
||||
import com.volmit.iris.util.matter.MatterSlice;
|
||||
@ -51,8 +51,6 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
|
||||
@ -60,18 +58,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
|
||||
public class Mantle {
|
||||
private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true");
|
||||
private final File dataFolder;
|
||||
@Getter
|
||||
private final int worldHeight;
|
||||
private final Map<Long, Long> lastUse;
|
||||
@Getter
|
||||
private final Map<Long, TectonicPlate> loadedRegions;
|
||||
private final HyperLock hyperLock;
|
||||
private final AtomicBoolean closed;
|
||||
private final MultiBurst ioBurst;
|
||||
private final AtomicBoolean ioTrim;
|
||||
private final AtomicBoolean ioTectonicUnload;
|
||||
private final AtomicDouble adjustedIdleDuration;
|
||||
private final KSet<Long> toUnload;
|
||||
|
||||
/**
|
||||
* Create a new mantle
|
||||
@ -87,10 +85,11 @@ public class Mantle {
|
||||
this.worldHeight = worldHeight;
|
||||
this.ioTrim = new AtomicBoolean(false);
|
||||
this.ioTectonicUnload = new AtomicBoolean(false);
|
||||
dataFolder.mkdirs();
|
||||
loadedRegions = new KMap<>();
|
||||
lastUse = new KMap<>();
|
||||
ioBurst = MultiBurst.burst;
|
||||
adjustedIdleDuration = new AtomicDouble(0);
|
||||
toUnload = new KSet<>();
|
||||
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
@ -103,7 +102,7 @@ public class Mantle {
|
||||
* @return the file
|
||||
*/
|
||||
public static File fileForRegion(File folder, int x, int z) {
|
||||
return fileForRegion(folder, key(x, z));
|
||||
return fileForRegion(folder, key(x, z), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,12 +112,28 @@ public class Mantle {
|
||||
* @param key the region key
|
||||
* @return the file
|
||||
*/
|
||||
public static File fileForRegion(File folder, Long key) {
|
||||
File f = new File(folder, "p." + key + ".ttp.lz4b");
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
public static File fileForRegion(File folder, Long key, boolean convert) {
|
||||
File f = oldFileForRegion(folder, key);
|
||||
File fv = new File(folder, "pv." + key + ".ttp.lz4b");
|
||||
if (f.exists() && !fv.exists() && convert)
|
||||
return f;
|
||||
|
||||
if (!fv.getParentFile().exists()) {
|
||||
fv.getParentFile().mkdirs();
|
||||
}
|
||||
return f;
|
||||
return fv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the old file for the given region
|
||||
*
|
||||
* @param folder the data folder
|
||||
* @param key the region key
|
||||
* @return the file
|
||||
*/
|
||||
public static File oldFileForRegion(File folder, Long key) {
|
||||
return new File(folder, "p." + key + ".ttp.lz4b");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +225,7 @@ public class Mantle {
|
||||
@RegionCoordinates
|
||||
public boolean hasTectonicPlate(int x, int z) {
|
||||
Long k = key(x, z);
|
||||
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k).exists();
|
||||
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k, true).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,21 +369,24 @@ public class Mantle {
|
||||
*/
|
||||
public synchronized void close() {
|
||||
Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
if (closed.get()) {
|
||||
if (closed.getAndSet(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closed.set(true);
|
||||
BurstExecutor b = ioBurst.burst(loadedRegions.size());
|
||||
for (Long i : loadedRegions.keySet()) {
|
||||
b.queue(() -> {
|
||||
try {
|
||||
loadedRegions.get(i).write(fileForRegion(dataFolder, i));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
hyperLock.disable();
|
||||
BurstExecutor b = ioBurst.burst(toUnload.size());
|
||||
loadedRegions.forEach((i, plate) -> b.queue(() -> {
|
||||
try {
|
||||
plate.close();
|
||||
plate.write(fileForRegion(dataFolder, i, false));
|
||||
oldFileForRegion(dataFolder, i).delete();
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
loadedRegions.clear();
|
||||
|
||||
try {
|
||||
b.complete();
|
||||
@ -376,7 +394,7 @@ public class Mantle {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
loadedRegions.clear();
|
||||
IO.delete(new File(dataFolder, ".tmp"));
|
||||
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
@ -392,16 +410,6 @@ public class Mantle {
|
||||
return numberOfEntries * bytesPerEntry;
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0);
|
||||
@Getter
|
||||
private final AtomicInteger forceAggressiveThreshold = new AtomicInteger(30);
|
||||
@Getter
|
||||
private final AtomicLong oldestTectonicPlate = new AtomicLong(0);
|
||||
private final ReentrantLock unloadLock = new ReentrantLock();
|
||||
@Getter
|
||||
private final KList<Long> toUnload = new KList<>();
|
||||
|
||||
/**
|
||||
* Save & unload regions that have not been used for more than the
|
||||
* specified amount of milliseconds
|
||||
@ -414,93 +422,81 @@ public class Mantle {
|
||||
}
|
||||
|
||||
adjustedIdleDuration.set(baseIdleDuration);
|
||||
|
||||
if (loadedRegions != null) {
|
||||
if (loadedRegions.size() > tectonicLimit) {
|
||||
// todo update this correctly and maybe do something when its above a 100%
|
||||
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
||||
}
|
||||
if (loadedRegions.size() > tectonicLimit) {
|
||||
// todo update this correctly and maybe do something when its above a 100%
|
||||
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
||||
}
|
||||
|
||||
ioTrim.set(true);
|
||||
unloadLock.lock();
|
||||
try {
|
||||
if (lastUse != null && IrisEngineSVC.instance != null) {
|
||||
if (!lastUse.isEmpty()) {
|
||||
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
|
||||
for (long i : new ArrayList<>(lastUse.keySet())) {
|
||||
double finalAdjustedIdleDuration = adjustedIdleDuration.get();
|
||||
hyperLock.withLong(i, () -> {
|
||||
Long lastUseTime = lastUse.get(i);
|
||||
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) {
|
||||
toUnload.add(i);
|
||||
Iris.debug("Tectonic Region added to unload");
|
||||
IrisEngineSVC.instance.trimActiveAlive.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
double adjustedIdleDuration = this.adjustedIdleDuration.get();
|
||||
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0));
|
||||
|
||||
if (lastUse.isEmpty()) return;
|
||||
double unloadTime = M.ms() - adjustedIdleDuration;
|
||||
for (long id : lastUse.keySet()) {
|
||||
hyperLock.withLong(id, () -> {
|
||||
Long lastUseTime = lastUse.get(id);
|
||||
if (lastUseTime != null && lastUseTime < unloadTime) {
|
||||
toUnload.add(id);
|
||||
Iris.debug("Tectonic Region added to unload");
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
} finally {
|
||||
ioTrim.set(false);
|
||||
unloadLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int unloadTectonicPlate(int tectonicLimit) {
|
||||
if (closed.get()) {
|
||||
throw new RuntimeException("The Mantle is closed");
|
||||
}
|
||||
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
unloadLock.lock();
|
||||
BurstExecutor burst = null;
|
||||
if (IrisEngineSVC.instance != null) {
|
||||
try {
|
||||
KList<Long> copy = toUnload.copy();
|
||||
if (!disableClear) toUnload.clear();
|
||||
burst = MultiBurst.burst.burst(copy.size());
|
||||
burst.setMulticore(copy.size() > tectonicLimit);
|
||||
for (int j = 0; j < copy.size(); j++) {
|
||||
Long id = copy.get(j);
|
||||
if (id == null) {
|
||||
Iris.error("Null id in unloadTectonicPlate at index " + j);
|
||||
continue;
|
||||
BurstExecutor burst = ioBurst.burst(toUnload.size());
|
||||
burst.setMulticore(toUnload.size() > tectonicLimit);
|
||||
|
||||
ioTectonicUnload.set(true);
|
||||
try {
|
||||
for (long id : toUnload) {
|
||||
burst.queue(() -> hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m == null) {
|
||||
Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
toUnload.remove(id);
|
||||
return;
|
||||
}
|
||||
|
||||
burst.queue(() ->
|
||||
hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m != null) {
|
||||
if (m.inUse()) {
|
||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||
if (disableClear) toUnload.remove(id);
|
||||
lastUse.put(id, M.ms());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id));
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
if (disableClear) toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
IrisEngineSVC.instance.unloadActiveAlive.reset();
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
burst.complete();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (burst != null)
|
||||
burst.complete();
|
||||
} finally {
|
||||
unloadLock.unlock();
|
||||
ioTectonicUnload.set(true);
|
||||
if (m.inUse()) {
|
||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||
lastUse.put(id, M.ms());
|
||||
toUnload.remove(id);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id, false));
|
||||
oldFileForRegion(dataFolder, id).delete();
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return i.get();
|
||||
burst.complete();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
burst.complete();
|
||||
} finally {
|
||||
ioTectonicUnload.set(false);
|
||||
}
|
||||
return i.get();
|
||||
}
|
||||
@ -516,7 +512,7 @@ public class Mantle {
|
||||
*/
|
||||
@RegionCoordinates
|
||||
private TectonicPlate get(int x, int z) {
|
||||
if (ioTrim.get()) {
|
||||
if (ioTrim.get() || ioTectonicUnload.get()) {
|
||||
try {
|
||||
return getSafe(x, z).get();
|
||||
} catch (InterruptedException e) {
|
||||
@ -576,7 +572,7 @@ public class Mantle {
|
||||
if (file.exists()) {
|
||||
try {
|
||||
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
|
||||
region = TectonicPlate.read(worldHeight, file);
|
||||
region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv."));
|
||||
|
||||
if (region.getX() != x || region.getZ() != z) {
|
||||
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
||||
@ -626,6 +622,14 @@ public class Mantle {
|
||||
return loadedRegions.size();
|
||||
}
|
||||
|
||||
public int getUnloadRegionCount() {
|
||||
return toUnload.size();
|
||||
}
|
||||
|
||||
public double getAdjustedIdleDuration() {
|
||||
return adjustedIdleDuration.get();
|
||||
}
|
||||
|
||||
public <T> void set(int x, int y, int z, MatterSlice<T> slice) {
|
||||
if (slice.isEmpty()) {
|
||||
return;
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.volmit.iris.util.mantle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||
@ -30,7 +31,8 @@ import lombok.Getter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
@ -45,7 +47,8 @@ public class MantleChunk {
|
||||
private final int z;
|
||||
private final AtomicIntegerArray flags;
|
||||
private final AtomicReferenceArray<Matter> sections;
|
||||
private final AtomicInteger ref = new AtomicInteger();
|
||||
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
|
||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Create a mantle chunk
|
||||
@ -72,11 +75,12 @@ public class MantleChunk {
|
||||
* @throws IOException shit happens
|
||||
* @throws ClassNotFoundException shit happens
|
||||
*/
|
||||
public MantleChunk(int sectionHeight, CountingDataInputStream din) throws IOException {
|
||||
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
|
||||
this(sectionHeight, din.readByte(), din.readByte());
|
||||
int s = din.readByte();
|
||||
int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din);
|
||||
|
||||
for (int i = 0; i < flags.length(); i++) {
|
||||
for (int i = 0; i < flags.length() && i < l; i++) {
|
||||
flags.set(i, din.readBoolean() ? 1 : 0);
|
||||
}
|
||||
|
||||
@ -85,6 +89,10 @@ public class MantleChunk {
|
||||
long size = din.readInt();
|
||||
if (size == 0) continue;
|
||||
long start = din.count();
|
||||
if (i >= sectionHeight) {
|
||||
din.skipTo(start + size);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
sections.set(i, Matter.readDin(din));
|
||||
@ -103,20 +111,27 @@ public class MantleChunk {
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
closed.set(true);
|
||||
ref.acquire(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public boolean inUse() {
|
||||
return ref.get() > 0;
|
||||
return ref.availablePermits() < Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public MantleChunk use() {
|
||||
ref.incrementAndGet();
|
||||
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||
ref.acquireUninterruptibly();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
ref.decrementAndGet();
|
||||
ref.release();
|
||||
}
|
||||
|
||||
public void flag(MantleFlag flag, boolean f) {
|
||||
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||
flags.set(flag.ordinal(), f ? 1 : 0);
|
||||
}
|
||||
|
||||
@ -201,6 +216,7 @@ public class MantleChunk {
|
||||
dos.writeByte(x);
|
||||
dos.writeByte(z);
|
||||
dos.writeByte(sections.length());
|
||||
Varint.writeUnsignedVarInt(flags.length(), dos);
|
||||
|
||||
for (int i = 0; i < flags.length(); i++) {
|
||||
dos.writeBoolean(flags.get(i) == 1);
|
||||
|
@ -19,13 +19,15 @@
|
||||
package com.volmit.iris.util.mantle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.engine.EnginePanic;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import lombok.Getter;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
@ -44,7 +46,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
* Tectonic Plates are fully atomic & thread safe
|
||||
*/
|
||||
public class TectonicPlate {
|
||||
private static final KSet<Thread> errors = new KSet<>();
|
||||
private static final ThreadLocal<Boolean> errors = ThreadLocal.withInitial(() -> false);
|
||||
public static final int MISSING = -1;
|
||||
public static final int CURRENT = 0;
|
||||
|
||||
private final int sectionHeight;
|
||||
private final AtomicReferenceArray<MantleChunk> chunks;
|
||||
@ -74,11 +78,12 @@ public class TectonicPlate {
|
||||
* @param din the data input
|
||||
* @throws IOException shit happens yo
|
||||
*/
|
||||
public TectonicPlate(int worldHeight, CountingDataInputStream din) throws IOException {
|
||||
public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException {
|
||||
this(worldHeight, din.readInt(), din.readInt());
|
||||
if (!din.markSupported())
|
||||
throw new IOException("Mark not supported!");
|
||||
|
||||
int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING;
|
||||
for (int i = 0; i < chunks.length(); i++) {
|
||||
long size = din.readInt();
|
||||
if (size == 0) continue;
|
||||
@ -86,7 +91,7 @@ public class TectonicPlate {
|
||||
|
||||
try {
|
||||
Iris.addPanic("read-chunk", "Chunk[" + i + "]");
|
||||
chunks.set(i, new MantleChunk(sectionHeight, din));
|
||||
chunks.set(i, new MantleChunk(v, sectionHeight, din));
|
||||
EnginePanic.saveLast();
|
||||
} catch (Throwable e) {
|
||||
long end = start + size;
|
||||
@ -103,7 +108,7 @@ public class TectonicPlate {
|
||||
}
|
||||
}
|
||||
|
||||
public static TectonicPlate read(int worldHeight, File file) throws IOException {
|
||||
public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException {
|
||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
||||
fc.lock();
|
||||
|
||||
@ -111,10 +116,10 @@ public class TectonicPlate {
|
||||
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
||||
BufferedInputStream bis = new BufferedInputStream(lz4);
|
||||
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
|
||||
return new TectonicPlate(worldHeight, din);
|
||||
return new TectonicPlate(worldHeight, din, versioned);
|
||||
}
|
||||
} finally {
|
||||
if (errors.remove(Thread.currentThread())) {
|
||||
if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
|
||||
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
|
||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
||||
fc.lock();
|
||||
@ -124,6 +129,7 @@ public class TectonicPlate {
|
||||
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
errors.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,6 +142,15 @@ public class TectonicPlate {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
for (int i = 0; i < chunks.length(); i++) {
|
||||
MantleChunk chunk = chunks.get(i);
|
||||
if (chunk != null) {
|
||||
chunk.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a chunk exists in this plate or not (same as get(x, z) != null)
|
||||
*
|
||||
@ -208,15 +223,8 @@ public class TectonicPlate {
|
||||
*/
|
||||
public void write(File file) throws IOException {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) {
|
||||
fc.lock();
|
||||
|
||||
OutputStream fos = Channels.newOutputStream(fc);
|
||||
try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(fos))) {
|
||||
write(dos);
|
||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||
}
|
||||
}
|
||||
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write);
|
||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,6 +236,7 @@ public class TectonicPlate {
|
||||
public void write(DataOutputStream dos) throws IOException {
|
||||
dos.writeInt(x);
|
||||
dos.writeInt(z);
|
||||
Varint.writeUnsignedVarInt(CURRENT, dos);
|
||||
|
||||
var bytes = new ByteArrayOutputStream(8192);
|
||||
var sub = new DataOutputStream(bytes);
|
||||
@ -249,6 +258,6 @@ public class TectonicPlate {
|
||||
}
|
||||
|
||||
public static void addError() {
|
||||
errors.add(Thread.currentThread());
|
||||
errors.set(true);
|
||||
}
|
||||
}
|
||||
|
@ -154,15 +154,16 @@ public interface Matter {
|
||||
matter.putSlice(type, slice);
|
||||
} catch (Throwable e) {
|
||||
long end = start + size;
|
||||
Iris.error("Failed to read matter slice, skipping it.");
|
||||
Iris.addPanic("read.byte.range", start + " " + end);
|
||||
Iris.addPanic("read.byte.current", din.count() + "");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.panic();
|
||||
|
||||
if (!(e instanceof ClassNotFoundException)) {
|
||||
Iris.error("Failed to read matter slice, skipping it.");
|
||||
Iris.addPanic("read.byte.range", start + " " + end);
|
||||
Iris.addPanic("read.byte.current", din.count() + "");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.panic();
|
||||
TectonicPlate.addError();
|
||||
}
|
||||
din.skipTo(end);
|
||||
TectonicPlate.addError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.data.B;
|
||||
import com.volmit.iris.util.data.IrisCustomData;
|
||||
import com.volmit.iris.util.data.palette.Palette;
|
||||
import com.volmit.iris.util.matter.Sliced;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -63,6 +63,6 @@ public class BlockMatter extends RawMatter<BlockData> {
|
||||
|
||||
@Override
|
||||
public BlockData readNode(DataInputStream din) throws IOException {
|
||||
return Bukkit.createBlockData(din.readUTF());
|
||||
return B.get(din.readUTF());
|
||||
}
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package com.volmit.iris.util.misc;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import oshi.SystemInfo;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
public class Hastebin {
|
||||
|
||||
public static void enviornment(CommandSender sender) {
|
||||
// Construct the server information
|
||||
StringBuilder sb = new StringBuilder();
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
KList<String> disks = new KList<>(getHardware.getDisk());
|
||||
KList<String> interfaces = new KList<>(getHardware.getInterfaces());
|
||||
KList<String> displays = new KList<>(getHardware.getEDID());
|
||||
KList<String> sensors = new KList<>(getHardware.getSensors());
|
||||
KList<String> gpus = new KList<>(getHardware.getGraphicsCards());
|
||||
KList<String> powersources = new KList<>(getHardware.getPowerSources());
|
||||
|
||||
KList<World> IrisWorlds = new KList<>();
|
||||
KList<World> BukkitWorlds = new KList<>();
|
||||
|
||||
for (World w : Bukkit.getServer().getWorlds()) {
|
||||
try {
|
||||
Engine engine = IrisToolbelt.access(w).getEngine();
|
||||
if (engine != null) {
|
||||
IrisWorlds.add(w);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
BukkitWorlds.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" -- == Iris Info == -- \n");
|
||||
sb.append("Iris Version Version: ").append(Iris.instance.getDescription().getVersion()).append("\n");
|
||||
sb.append("- Iris Worlds");
|
||||
for (World w : IrisWorlds.copy()) {
|
||||
sb.append(" - ").append(w.getName());
|
||||
}
|
||||
sb.append("- Bukkit Worlds");
|
||||
for (World w : BukkitWorlds.copy()) {
|
||||
sb.append(" - ").append(w.getName());
|
||||
}
|
||||
sb.append(" -- == Platform Overview == -- " + "\n");
|
||||
sb.append("Server Type: ").append(Bukkit.getVersion()).append("\n");
|
||||
sb.append("Server Uptime: ").append(Form.stampTime(systemInfo.getOperatingSystem().getSystemUptime())).append("\n");
|
||||
sb.append("Version: ").append(Platform.getVersion()).append(" - Platform: ").append(Platform.getName()).append("\n");
|
||||
sb.append("Java Vendor: ").append(Platform.ENVIRONMENT.getJavaVendor()).append(" - Java Version: ").append(Platform.ENVIRONMENT.getJavaVersion()).append("\n");
|
||||
sb.append(" -- == Processor Overview == -- " + "\n");
|
||||
sb.append("CPU Model: ").append(getHardware.getCPUModel());
|
||||
sb.append("CPU Architecture: ").append(Platform.CPU.getArchitecture()).append(" Available Processors: ").append(Platform.CPU.getAvailableProcessors()).append("\n");
|
||||
sb.append("CPU Load: ").append(Form.pc(Platform.CPU.getCPULoad())).append(" CPU Live Process Load: ").append(Form.pc(Platform.CPU.getLiveProcessCPULoad())).append("\n");
|
||||
sb.append("-=" + " Graphics " + "=- " + "\n");
|
||||
for (String gpu : gpus) {
|
||||
sb.append(" ").append(gpu).append("\n");
|
||||
}
|
||||
sb.append(" -- == Memory Information == -- " + "\n");
|
||||
sb.append("Physical Memory - Total: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getUsedMemory())).append("\n");
|
||||
sb.append("Virtual Memory - Total: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getUsedMemory())).append("\n");
|
||||
sb.append(" -- == Storage Information == -- " + "\n");
|
||||
for (String disk : disks) {
|
||||
sb.append(" ").append(sb.append(disk)).append("\n");
|
||||
}
|
||||
sb.append(" -- == Interface Information == -- "+ "\n" );
|
||||
for (String inter : interfaces) {
|
||||
sb.append(" ").append(inter).append("\n");
|
||||
}
|
||||
sb.append(" -- == Display Information == -- "+ "\n" );
|
||||
for (String display : displays) {
|
||||
sb.append(display).append("\n");
|
||||
}
|
||||
sb.append(" -- == Sensor Information == -- " + "\n");
|
||||
for (String sensor : sensors) {
|
||||
sb.append(" ").append(sensor).append("\n");
|
||||
}
|
||||
sb.append(" -- == Power Information == -- " + "\n");
|
||||
for (String power : powersources) {
|
||||
sb.append(" ").append(power).append("\n");
|
||||
}
|
||||
|
||||
try {
|
||||
String hastebinUrl = uploadToHastebin(sb.toString());
|
||||
|
||||
// Create the clickable message
|
||||
TextComponent message = new TextComponent("[Link]");
|
||||
TextComponent link = new TextComponent(hastebinUrl);
|
||||
link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, hastebinUrl));
|
||||
message.addExtra(link);
|
||||
|
||||
// Send the clickable message to the player
|
||||
sender.spigot().sendMessage(message);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(C.DARK_RED + "Failed to upload server information to Hastebin.");
|
||||
}
|
||||
}
|
||||
|
||||
private static String uploadToHastebin(String content) throws Exception {
|
||||
URL url = new URL("https://paste.bytecode.ninja/documents");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "text/plain");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
|
||||
wr.writeBytes(content);
|
||||
wr.flush();
|
||||
wr.close();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String response = br.readLine();
|
||||
br.close();
|
||||
|
||||
return "https://paste.bytecode.ninja/" + response.split("\"")[3];
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
package com.volmit.iris.util.misc;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class Platform {
|
||||
public static String getVersion() {
|
||||
return getSystem().getVersion();
|
||||
}
|
||||
|
||||
public static String getName() {
|
||||
return getSystem().getName();
|
||||
}
|
||||
|
||||
private static OperatingSystemMXBean getSystem() {
|
||||
return (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
}
|
||||
|
||||
public static class ENVIRONMENT {
|
||||
public static boolean canRunBatch() {
|
||||
return getSystem().getName().toLowerCase().contains("windows");
|
||||
}
|
||||
|
||||
public static String getJavaHome() {
|
||||
return System.getProperty("java.home");
|
||||
}
|
||||
|
||||
public static String getJavaVendor() {
|
||||
return System.getProperty("java.vendor");
|
||||
}
|
||||
|
||||
public static String getJavaVersion() {
|
||||
return System.getProperty("java.version");
|
||||
}
|
||||
}
|
||||
|
||||
public static class STORAGE {
|
||||
public static long getAbsoluteTotalSpace() {
|
||||
long t = 0;
|
||||
|
||||
for (File i : getRoots()) {
|
||||
t += getTotalSpace(i);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static long getTotalSpace() {
|
||||
return getTotalSpace(new File("."));
|
||||
}
|
||||
|
||||
public static long getTotalSpace(File root) {
|
||||
return root.getTotalSpace();
|
||||
}
|
||||
|
||||
public static long getAbsoluteFreeSpace() {
|
||||
long t = 0;
|
||||
|
||||
for (File i : getRoots()) {
|
||||
t += getFreeSpace(i);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static long getFreeSpace() {
|
||||
return getFreeSpace(new File("."));
|
||||
}
|
||||
|
||||
public static long getFreeSpace(File root) {
|
||||
return root.getFreeSpace();
|
||||
}
|
||||
|
||||
public static long getUsedSpace() {
|
||||
return getTotalSpace() - getFreeSpace();
|
||||
}
|
||||
|
||||
public static long getUsedSpace(File root) {
|
||||
return getTotalSpace(root) - getFreeSpace(root);
|
||||
}
|
||||
|
||||
public static long getAbsoluteUsedSpace() {
|
||||
return getAbsoluteTotalSpace() - getAbsoluteFreeSpace();
|
||||
}
|
||||
|
||||
public static File[] getRoots() {
|
||||
return File.listRoots();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MEMORY {
|
||||
public static class PHYSICAL {
|
||||
public static long getTotalMemory() {
|
||||
return getSystem().getTotalPhysicalMemorySize();
|
||||
}
|
||||
|
||||
public static long getFreeMemory() {
|
||||
return getSystem().getFreePhysicalMemorySize();
|
||||
}
|
||||
|
||||
public static long getUsedMemory() {
|
||||
return getTotalMemory() - getFreeMemory();
|
||||
}
|
||||
}
|
||||
|
||||
public static class VIRTUAL {
|
||||
public static long getTotalMemory() {
|
||||
return getSystem().getTotalSwapSpaceSize();
|
||||
}
|
||||
|
||||
public static long getFreeMemory() {
|
||||
return getSystem().getFreeSwapSpaceSize();
|
||||
}
|
||||
|
||||
public static long getUsedMemory() {
|
||||
return getTotalMemory() - getFreeMemory();
|
||||
}
|
||||
|
||||
public static long getCommittedVirtualMemory() {
|
||||
return getSystem().getCommittedVirtualMemorySize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CPU {
|
||||
public static int getAvailableProcessors() {
|
||||
return getSystem().getAvailableProcessors();
|
||||
}
|
||||
|
||||
public static double getCPULoad() {
|
||||
return getSystem().getSystemCpuLoad();
|
||||
}
|
||||
|
||||
public static double getLiveProcessCPULoad() {
|
||||
return getSystem().getProcessCpuLoad();
|
||||
}
|
||||
|
||||
public static String getArchitecture() {
|
||||
return getSystem().getArch();
|
||||
}
|
||||
}
|
||||
}
|
@ -143,5 +143,6 @@ public class HyperLock {
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
locks.values().forEach(ReentrantLock::lock);
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class MultiBurst implements ExecutorService {
|
||||
private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000);
|
||||
public static final MultiBurst burst = new MultiBurst();
|
||||
private final AtomicLong last;
|
||||
private final String name;
|
||||
@ -231,7 +232,7 @@ public class MultiBurst implements ExecutorService {
|
||||
try {
|
||||
while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||
Iris.info("Still waiting to shutdown burster...");
|
||||
if (p.getMilliseconds() > 7000) {
|
||||
if (p.getMilliseconds() > TIMEOUT) {
|
||||
Iris.warn("Forcing Shutdown...");
|
||||
|
||||
try {
|
||||
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.particle;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Simple Bukkit Particles API with 1.7 to 1.13.2 support !
|
||||
* <p>
|
||||
* You can find the project on <a href="https://github.com/MrMicky-FR/FastParticles">GitHub</a>
|
||||
*
|
||||
* @author MrMicky
|
||||
*/
|
||||
public final class FastParticle {
|
||||
|
||||
private static final ParticleSender PARTICLE_SENDER;
|
||||
|
||||
static {
|
||||
if (FastReflection.optionalClass("org.bukkit.Particle$DustOptions").isPresent()) {
|
||||
PARTICLE_SENDER = new ParticleSender.ParticleSender1_13();
|
||||
} else if (FastReflection.optionalClass("org.bukkit.Particle").isPresent()) {
|
||||
PARTICLE_SENDER = new ParticleSender.ParticleSenderImpl();
|
||||
} else {
|
||||
PARTICLE_SENDER = new ParticleSenderLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
private FastParticle() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* Worlds methods
|
||||
*/
|
||||
public static void spawnParticle(World world, ParticleType particle, Location location, int count) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count) {
|
||||
spawnParticle(world, particle, x, y, z, count, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count, T data) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
|
||||
T data) {
|
||||
spawnParticle(world, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
|
||||
double offsetY, double offsetZ) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ) {
|
||||
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
|
||||
double offsetX, double offsetY, double offsetZ, T data) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY,
|
||||
offsetZ, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, T data) {
|
||||
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
|
||||
double offsetY, double offsetZ, double extra) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra) {
|
||||
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
sendParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Player methods
|
||||
*/
|
||||
public static void spawnParticle(Player player, ParticleType particle, Location location, int count) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count);
|
||||
}
|
||||
|
||||
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count) {
|
||||
spawnParticle(player, particle, x, y, z, count, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count, T data) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
|
||||
T data) {
|
||||
spawnParticle(player, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
|
||||
}
|
||||
|
||||
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
|
||||
double offsetY, double offsetZ) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ) {
|
||||
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
|
||||
double offsetX, double offsetY, double offsetZ, T data) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, T data) {
|
||||
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
|
||||
}
|
||||
|
||||
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
|
||||
double offsetY, double offsetZ, double extra) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
|
||||
}
|
||||
|
||||
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra) {
|
||||
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
sendParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
private static void sendParticle(Object receiver, ParticleType particle, double x, double y, double z, int count,
|
||||
double offsetX, double offsetY, double offsetZ, double extra, Object data) {
|
||||
if (!particle.isSupported()) {
|
||||
throw new IllegalArgumentException("The particle '" + particle + "' is not compatible with your server version");
|
||||
}
|
||||
|
||||
PARTICLE_SENDER.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.particle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Small reflection class to use CraftBukkit and NMS
|
||||
*
|
||||
* @author MrMicky
|
||||
*/
|
||||
public final class FastReflection {
|
||||
|
||||
public static final String OBC_PACKAGE = "org.bukkit.craftbukkit";
|
||||
public static final String NMS_PACKAGE = "net.minecraft.server";
|
||||
|
||||
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1);
|
||||
|
||||
private FastReflection() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static String nmsClassName(String className) {
|
||||
return NMS_PACKAGE + '.' + VERSION + '.' + className;
|
||||
}
|
||||
|
||||
public static Class<?> nmsClass(String className) throws ClassNotFoundException {
|
||||
return Class.forName(nmsClassName(className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> nmsOptionalClass(String className) {
|
||||
return optionalClass(nmsClassName(className));
|
||||
}
|
||||
|
||||
public static String obcClassName(String className) {
|
||||
return OBC_PACKAGE + '.' + VERSION + '.' + className;
|
||||
}
|
||||
|
||||
public static Class<?> obcClass(String className) throws ClassNotFoundException {
|
||||
return Class.forName(obcClassName(className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> obcOptionalClass(String className) {
|
||||
return optionalClass(obcClassName(className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> optionalClass(String className) {
|
||||
try {
|
||||
return Optional.of(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
Iris.reportError(e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static Object enumValueOf(Class<?> enumClass, String enumName) {
|
||||
return Enum.valueOf((Class<Enum>) enumClass, enumName.toUpperCase());
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.particle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.material.MaterialData;
|
||||
|
||||
/**
|
||||
* Particle sender using the Bukkit particles API for 1.9+ servers
|
||||
*
|
||||
* @author MrMicky
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
interface ParticleSender {
|
||||
|
||||
void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data);
|
||||
|
||||
Object getParticle(ParticleType particle);
|
||||
|
||||
boolean isValidData(Object particle, Object data);
|
||||
|
||||
default double color(double color) {
|
||||
return color / 255.0;
|
||||
}
|
||||
|
||||
class ParticleSenderImpl implements ParticleSender {
|
||||
|
||||
@Override
|
||||
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) {
|
||||
Particle bukkitParticle = Particle.valueOf(particle.toString());
|
||||
|
||||
if (data instanceof Color) {
|
||||
if (particle.getDataType() == Color.class) {
|
||||
Color color = (Color) data;
|
||||
count = 0;
|
||||
offsetX = color(color.getRed());
|
||||
offsetY = color(color.getGreen());
|
||||
offsetZ = color(color.getBlue());
|
||||
extra = 1.0;
|
||||
}
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (receiver instanceof World) {
|
||||
((World) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
} else if (receiver instanceof Player) {
|
||||
((Player) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Particle getParticle(ParticleType particle) {
|
||||
try {
|
||||
return Particle.valueOf(particle.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Iris.reportError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidData(Object particle, Object data) {
|
||||
return isValidDataBukkit((Particle) particle, data);
|
||||
}
|
||||
|
||||
public boolean isValidDataBukkit(Particle particle, Object data) {
|
||||
return particle.getDataType() == Void.class || particle.getDataType().isInstance(data);
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleSender1_13 extends ParticleSenderImpl {
|
||||
@Override
|
||||
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) {
|
||||
Particle bukkitParticle = Particle.valueOf(particle.toString());
|
||||
|
||||
if (bukkitParticle.getDataType() == Particle.DustOptions.class) {
|
||||
if (data instanceof Color) {
|
||||
data = new Particle.DustOptions((Color) data, 1);
|
||||
} else if (data == null) {
|
||||
data = new Particle.DustOptions(Color.RED, 1);
|
||||
}
|
||||
} else if (bukkitParticle.getDataType() == BlockData.class && data instanceof MaterialData) {
|
||||
data = Bukkit.createBlockData(((MaterialData) data).getItemType());
|
||||
}
|
||||
|
||||
super.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidDataBukkit(Particle particle, Object data) {
|
||||
if (particle.getDataType() == Particle.DustOptions.class && data instanceof Color) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (particle.getDataType() == BlockData.class && data instanceof MaterialData) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.isValidDataBukkit(particle, data);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.particle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.MaterialData;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Legacy particle sender with NMS for 1.7/1.8 servers
|
||||
*
|
||||
* @author MrMicky
|
||||
*/
|
||||
@SuppressWarnings({"deprecation", "JavaReflectionInvocation"})
|
||||
class ParticleSenderLegacy implements ParticleSender {
|
||||
|
||||
private static final boolean SERVER_IS_1_8;
|
||||
|
||||
private static final Constructor<?> PACKET_PARTICLE;
|
||||
private static final Class<?> ENUM_PARTICLE;
|
||||
|
||||
private static final Method WORLD_GET_HANDLE;
|
||||
private static final Method WORLD_SEND_PARTICLE;
|
||||
|
||||
private static final Method PLAYER_GET_HANDLE;
|
||||
private static final Field PLAYER_CONNECTION;
|
||||
private static final Method SEND_PACKET;
|
||||
private static final int[] EMPTY = new int[0];
|
||||
|
||||
static {
|
||||
ENUM_PARTICLE = FastReflection.nmsOptionalClass("EnumParticle").orElse(null);
|
||||
SERVER_IS_1_8 = ENUM_PARTICLE != null;
|
||||
|
||||
try {
|
||||
Class<?> packetParticleClass = FastReflection.nmsClass("PacketPlayOutWorldParticles");
|
||||
Class<?> playerClass = FastReflection.nmsClass("EntityPlayer");
|
||||
Class<?> playerConnectionClass = FastReflection.nmsClass("PlayerConnection");
|
||||
Class<?> worldClass = FastReflection.nmsClass("WorldServer");
|
||||
Class<?> entityPlayerClass = FastReflection.nmsClass("EntityPlayer");
|
||||
|
||||
Class<?> craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer");
|
||||
Class<?> craftWorldClass = FastReflection.obcClass("CraftWorld");
|
||||
|
||||
if (SERVER_IS_1_8) {
|
||||
PACKET_PARTICLE = packetParticleClass.getConstructor(ENUM_PARTICLE, boolean.class, float.class,
|
||||
float.class, float.class, float.class, float.class, float.class, float.class, int.class,
|
||||
int[].class);
|
||||
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("sendParticles", entityPlayerClass, ENUM_PARTICLE,
|
||||
boolean.class, double.class, double.class, double.class, int.class, double.class, double.class,
|
||||
double.class, double.class, int[].class);
|
||||
} else {
|
||||
PACKET_PARTICLE = packetParticleClass.getConstructor(String.class, float.class, float.class, float.class,
|
||||
float.class, float.class, float.class, float.class, int.class);
|
||||
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("a", String.class, double.class, double.class,
|
||||
double.class, int.class, double.class, double.class, double.class, double.class);
|
||||
}
|
||||
|
||||
WORLD_GET_HANDLE = craftWorldClass.getDeclaredMethod("getHandle");
|
||||
PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle");
|
||||
PLAYER_CONNECTION = playerClass.getField("playerConnection");
|
||||
SEND_PACKET = playerConnectionClass.getMethod("sendPacket", FastReflection.nmsClass("Packet"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY,
|
||||
double offsetZ, double extra, Object data) {
|
||||
try {
|
||||
int[] datas = toData(particle, data);
|
||||
|
||||
if (data instanceof Color) {
|
||||
if (particle.getDataType() == Color.class) {
|
||||
Color color = (Color) data;
|
||||
count = 0;
|
||||
offsetX = color(color.getRed());
|
||||
offsetY = color(color.getGreen());
|
||||
offsetZ = color(color.getBlue());
|
||||
extra = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (receiver instanceof World) {
|
||||
Object worldServer = WORLD_GET_HANDLE.invoke(receiver);
|
||||
|
||||
if (SERVER_IS_1_8) {
|
||||
WORLD_SEND_PARTICLE.invoke(worldServer, null, getEnumParticle(particle), true, x, y, z, count, offsetX, offsetY, offsetZ, extra, datas);
|
||||
} else {
|
||||
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
|
||||
WORLD_SEND_PARTICLE.invoke(worldServer, particleName, x, y, z, count, offsetX, offsetY, offsetZ, extra);
|
||||
}
|
||||
} else if (receiver instanceof Player) {
|
||||
Object packet;
|
||||
|
||||
if (SERVER_IS_1_8) {
|
||||
packet = PACKET_PARTICLE.newInstance(getEnumParticle(particle), true, (float) x, (float) y,
|
||||
(float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, datas);
|
||||
} else {
|
||||
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
|
||||
packet = PACKET_PARTICLE.newInstance(particleName, (float) x, (float) y, (float) z,
|
||||
(float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
|
||||
}
|
||||
|
||||
Object entityPlayer = PLAYER_GET_HANDLE.invoke(receiver);
|
||||
Object playerConnection = PLAYER_CONNECTION.get(entityPlayer);
|
||||
SEND_PACKET.invoke(playerConnection, packet);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
Iris.reportError(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidData(Object particle, Object data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParticle(ParticleType particle) {
|
||||
if (!SERVER_IS_1_8) {
|
||||
return particle.getLegacyName();
|
||||
}
|
||||
|
||||
try {
|
||||
return getEnumParticle(particle);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Iris.reportError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getEnumParticle(ParticleType particleType) {
|
||||
return FastReflection.enumValueOf(ENUM_PARTICLE, particleType.toString());
|
||||
}
|
||||
|
||||
private int[] toData(ParticleType particle, Object data) {
|
||||
Class<?> dataType = particle.getDataType();
|
||||
if (dataType == ItemStack.class) {
|
||||
if (!(data instanceof ItemStack itemStack)) {
|
||||
return SERVER_IS_1_8 ? new int[2] : new int[]{1, 0};
|
||||
}
|
||||
|
||||
return new int[]{itemStack.getType().getId(), itemStack.getDurability()};
|
||||
}
|
||||
|
||||
if (dataType == MaterialData.class) {
|
||||
if (!(data instanceof MaterialData materialData)) {
|
||||
return SERVER_IS_1_8 ? new int[1] : new int[]{1, 0};
|
||||
}
|
||||
|
||||
if (SERVER_IS_1_8) {
|
||||
return new int[]{materialData.getItemType().getId() + (materialData.getData() << 12)};
|
||||
} else {
|
||||
return new int[]{materialData.getItemType().getId(), materialData.getData()};
|
||||
}
|
||||
}
|
||||
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.particle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.MaterialData;
|
||||
|
||||
/**
|
||||
* @author MrMicky
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public enum ParticleType {
|
||||
|
||||
// 1.7+
|
||||
EXPLOSION_NORMAL("explode", "poof"),
|
||||
EXPLOSION_LARGE("largeexplode", "explosion"),
|
||||
EXPLOSION_HUGE("hugeexplosion", "explosion_emitter"),
|
||||
FIREWORKS_SPARK("fireworksSpark", "firework"),
|
||||
WATER_BUBBLE("bubble", "bubble"),
|
||||
WATER_SPLASH("splash", "splash"),
|
||||
WATER_WAKE("wake", "fishing"),
|
||||
SUSPENDED("suspended", "underwater"),
|
||||
SUSPENDED_DEPTH("depthsuspend", "underwater"),
|
||||
CRIT("crit", "crit"),
|
||||
CRIT_MAGIC("magicCrit", "enchanted_hit"),
|
||||
SMOKE_NORMAL("smoke", "smoke"),
|
||||
SMOKE_LARGE("largesmoke", "large_smoke"),
|
||||
SPELL("spell", "effect"),
|
||||
SPELL_INSTANT("instantSpell", "instant_effect"),
|
||||
SPELL_MOB("mobSpell", "entity_effect"),
|
||||
SPELL_MOB_AMBIENT("mobSpellAmbient", "ambient_entity_effect"),
|
||||
SPELL_WITCH("witchMagic", "witch"),
|
||||
DRIP_WATER("dripWater", "dripping_water"),
|
||||
DRIP_LAVA("dripLava", "dripping_lava"),
|
||||
VILLAGER_ANGRY("angryVillager", "angry_villager"),
|
||||
VILLAGER_HAPPY("happyVillager", "happy_villager"),
|
||||
TOWN_AURA("townaura", "mycelium"),
|
||||
NOTE("note", "note"),
|
||||
PORTAL("portal", "portal"),
|
||||
ENCHANTMENT_TABLE("enchantmenttable", "enchant"),
|
||||
FLAME("flame", "flame"),
|
||||
LAVA("lava", "lava"),
|
||||
// FOOTSTEP("footstep", null),
|
||||
CLOUD("cloud", "cloud"),
|
||||
REDSTONE("reddust", "dust"),
|
||||
SNOWBALL("snowballpoof", "item_snowball"),
|
||||
SNOW_SHOVEL("snowshovel", "item_snowball"),
|
||||
SLIME("slime", "item_slime"),
|
||||
HEART("heart", "heart"),
|
||||
ITEM_CRACK("iconcrack", "item"),
|
||||
BLOCK_CRACK("blockcrack", "block"),
|
||||
BLOCK_DUST("blockdust", "block"),
|
||||
|
||||
// 1.8+
|
||||
BARRIER("barrier", "barrier", 8),
|
||||
WATER_DROP("droplet", "rain", 8),
|
||||
MOB_APPEARANCE("mobappearance", "elder_guardian", 8),
|
||||
// ITEM_TAKE("take", null, 8),
|
||||
|
||||
// 1.9+
|
||||
DRAGON_BREATH("dragonbreath", "dragon_breath", 9),
|
||||
END_ROD("endRod", "end_rod", 9),
|
||||
DAMAGE_INDICATOR("damageIndicator", "damage_indicator", 9),
|
||||
SWEEP_ATTACK("sweepAttack", "sweep_attack", 9),
|
||||
|
||||
// 1.10+
|
||||
FALLING_DUST("fallingdust", "falling_dust", 10),
|
||||
|
||||
// 1.11+
|
||||
TOTEM("totem", "totem_of_undying", 11),
|
||||
SPIT("spit", "spit", 11),
|
||||
|
||||
// 1.13+
|
||||
SQUID_INK(13),
|
||||
BUBBLE_POP(13),
|
||||
CURRENT_DOWN(13),
|
||||
BUBBLE_COLUMN_UP(13),
|
||||
NAUTILUS(13),
|
||||
DOLPHIN(13),
|
||||
|
||||
// 1.14+
|
||||
SNEEZE(14),
|
||||
CAMPFIRE_COSY_SMOKE(14),
|
||||
CAMPFIRE_SIGNAL_SMOKE(14),
|
||||
COMPOSTER(14),
|
||||
FLASH(14),
|
||||
FALLING_LAVA(14),
|
||||
LANDING_LAVA(14),
|
||||
FALLING_WATER(14),
|
||||
|
||||
// 1.15+
|
||||
DRIPPING_HONEY(15),
|
||||
FALLING_HONEY(15),
|
||||
LANDING_HONEY(15),
|
||||
FALLING_NECTAR(15);
|
||||
|
||||
private static final int SERVER_VERSION_ID;
|
||||
|
||||
static {
|
||||
String ver = FastReflection.VERSION;
|
||||
SERVER_VERSION_ID = ver.charAt(4) == '_' ? Character.getNumericValue(ver.charAt(3)) : Integer.parseInt(ver.substring(3, 5));
|
||||
}
|
||||
|
||||
private final String legacyName;
|
||||
private final String name;
|
||||
private final int minimumVersion;
|
||||
|
||||
// 1.7 particles
|
||||
ParticleType(String legacyName, String name) {
|
||||
this(legacyName, name, -1);
|
||||
}
|
||||
|
||||
// 1.13+ particles
|
||||
ParticleType(int minimumVersion) {
|
||||
this.legacyName = null;
|
||||
this.name = name().toLowerCase();
|
||||
this.minimumVersion = minimumVersion;
|
||||
}
|
||||
|
||||
// 1.8-1.12 particles
|
||||
ParticleType(String legacyName, String name, int minimumVersion) {
|
||||
this.legacyName = legacyName;
|
||||
this.name = name;
|
||||
this.minimumVersion = minimumVersion;
|
||||
}
|
||||
|
||||
public static ParticleType getParticle(String particleName) {
|
||||
try {
|
||||
return ParticleType.valueOf(particleName.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Iris.reportError(e);
|
||||
for (ParticleType particle : values()) {
|
||||
if (particle.getName().equalsIgnoreCase(particleName)) {
|
||||
return particle;
|
||||
}
|
||||
|
||||
if (particle.hasLegacyName() && particle.getLegacyName().equalsIgnoreCase(particleName)) {
|
||||
return particle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasLegacyName() {
|
||||
return legacyName != null;
|
||||
}
|
||||
|
||||
public String getLegacyName() {
|
||||
if (!hasLegacyName()) {
|
||||
throw new IllegalStateException("Particle " + name() + " don't have legacy name");
|
||||
}
|
||||
return legacyName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
return minimumVersion <= 0 || SERVER_VERSION_ID >= minimumVersion;
|
||||
}
|
||||
|
||||
public Class<?> getDataType() {
|
||||
return switch (this) {
|
||||
case ITEM_CRACK -> ItemStack.class;
|
||||
case BLOCK_CRACK, BLOCK_DUST, FALLING_DUST ->
|
||||
//noinspection deprecation
|
||||
MaterialData.class;
|
||||
case REDSTONE -> Color.class;
|
||||
default -> Void.class;
|
||||
};
|
||||
}
|
||||
}
|
@ -264,7 +264,7 @@ public class VolmitSender implements CommandSender {
|
||||
private Component createNoPrefixComponent(String message) {
|
||||
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
|
||||
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(message));
|
||||
return MiniMessage.miniMessage().deserialize(t);
|
||||
return MiniMessage.miniMessage().deserialize(C.mini(t));
|
||||
}
|
||||
|
||||
String t = C.translateAlternateColorCodes('&', message);
|
||||
@ -273,13 +273,13 @@ public class VolmitSender implements CommandSender {
|
||||
}
|
||||
|
||||
private Component createNoPrefixComponentNoProcessing(String message) {
|
||||
return MiniMessage.builder().postProcessor(c -> c).build().deserialize(message);
|
||||
return MiniMessage.builder().postProcessor(c -> c).build().deserialize(C.mini(message));
|
||||
}
|
||||
|
||||
private Component createComponent(String message) {
|
||||
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
|
||||
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message));
|
||||
return MiniMessage.miniMessage().deserialize(t);
|
||||
return MiniMessage.miniMessage().deserialize(C.mini(t));
|
||||
}
|
||||
|
||||
String t = C.translateAlternateColorCodes('&', getTag() + message);
|
||||
@ -290,11 +290,11 @@ public class VolmitSender implements CommandSender {
|
||||
private Component createComponentRaw(String message) {
|
||||
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
|
||||
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message));
|
||||
return MiniMessage.miniMessage().deserialize(t);
|
||||
return MiniMessage.miniMessage().deserialize(C.mini(t));
|
||||
}
|
||||
|
||||
String t = C.translateAlternateColorCodes('&', getTag() + message);
|
||||
return MiniMessage.miniMessage().deserialize(t);
|
||||
return MiniMessage.miniMessage().deserialize(C.mini(t));
|
||||
}
|
||||
|
||||
public <T> void showWaiting(String passive, CompletableFuture<T> f) {
|
||||
|
@ -36,7 +36,6 @@ public abstract class Looper extends Thread {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(m);
|
||||
} catch (InterruptedException e) {
|
||||
Iris.reportError(e);
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.volmit.iris.util.sentry;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import io.sentry.Attachment;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class Attachments {
|
||||
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
|
||||
public static final Attachment PLUGINS = jsonProvider(Attachments::plugins, "plugins.json");
|
||||
|
||||
public static Attachment json(Object object, String name) {
|
||||
return new Attachment(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true);
|
||||
}
|
||||
|
||||
public static Attachment jsonProvider(Callable<Object> object, String name) {
|
||||
return new Attachment(() -> GSON.toJson(object.call()).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true);
|
||||
}
|
||||
|
||||
private static KMap<String, Object> plugins() {
|
||||
KMap<String, String> enabled = new KMap<>();
|
||||
KMap<String, String> disabled = new KMap<>();
|
||||
|
||||
var pm = Bukkit.getPluginManager();
|
||||
for (var plugin : pm.getPlugins()) {
|
||||
if (plugin.isEnabled()) {
|
||||
enabled.put(plugin.getName(), plugin.getDescription().getVersion());
|
||||
} else {
|
||||
disabled.put(plugin.getName(), plugin.getDescription().getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
return new KMap<String, Object>()
|
||||
.qput("enabled", enabled)
|
||||
.qput("disabled", disabled);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.volmit.iris.util.sentry;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import io.sentry.ILogger;
|
||||
import io.sentry.SentryLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class IrisLogger implements ILogger {
|
||||
@Override
|
||||
public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) {
|
||||
Iris.msg(String.format("%s: %s", level, String.format(message, args)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
log(level, message);
|
||||
} else {
|
||||
Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(@NotNull SentryLevel level, @Nullable Throwable throwable, @NotNull String message, @Nullable Object... args) {
|
||||
if (throwable == null) {
|
||||
log(level, message, args);
|
||||
} else {
|
||||
Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@Nullable SentryLevel level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private @NotNull String captureStackTrace(@NotNull Throwable throwable) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(stringWriter);
|
||||
throwable.printStackTrace(printWriter);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
59
core/src/main/java/com/volmit/iris/util/sentry/ServerID.java
Normal file
59
core/src/main/java/com/volmit/iris/util/sentry/ServerID.java
Normal file
@ -0,0 +1,59 @@
|
||||
package com.volmit.iris.util.sentry;
|
||||
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import io.sentry.protocol.User;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bukkit.Bukkit;
|
||||
import oshi.SystemInfo;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class ServerID {
|
||||
public static final String ID = calculate();
|
||||
|
||||
public static User asUser() {
|
||||
User u = new User();
|
||||
u.setId(ID);
|
||||
return u;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String calculate() {
|
||||
Digest md = new Digest();
|
||||
md.update(System.getProperty("java.vm.name"));
|
||||
md.update(System.getProperty("java.vm.version"));
|
||||
md.update(new SystemInfo().getHardware().getProcessor().toString());
|
||||
md.update(Runtime.getRuntime().maxMemory());
|
||||
for (var p : Bukkit.getPluginManager().getPlugins()) {
|
||||
md.update(p.getName());
|
||||
}
|
||||
|
||||
return IO.bytesToHex(md.digest());
|
||||
}
|
||||
|
||||
private static final class Digest {
|
||||
private final MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
private final byte[] buffer = new byte[8];
|
||||
private final ByteBuffer wrapped = ByteBuffer.wrap(buffer);
|
||||
|
||||
private Digest() throws NoSuchAlgorithmException {
|
||||
}
|
||||
|
||||
public void update(String string) {
|
||||
if (string == null) return;
|
||||
md.update(string.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public void update(long Long) {
|
||||
wrapped.putLong(0, Long);
|
||||
md.update(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public byte[] digest() {
|
||||
return md.digest();
|
||||
}
|
||||
}
|
||||
}
|
@ -23,5 +23,5 @@ libraries:
|
||||
commands:
|
||||
iris:
|
||||
aliases: [ ir, irs ]
|
||||
api-version: '${apiversion}'
|
||||
api-version: '${apiVersion}'
|
||||
hotload-dependencies: false
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
41
gradlew
vendored
41
gradlew
vendored
@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@ -80,13 +80,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@ -133,22 +131,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
@ -205,6 +214,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
35
gradlew.bat
vendored
35
gradlew.bat
vendored
@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
@ -0,0 +1,169 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R4;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisBiome;
|
||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
private final long seed;
|
||||
private final Engine engine;
|
||||
private final Registry<Biome> biomeCustomRegistry;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final RNG rng;
|
||||
private final KMap<String, Holder<Biome>> customBiomes;
|
||||
|
||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||
this.engine = engine;
|
||||
this.seed = seed;
|
||||
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
|
||||
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
|
||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
||||
}
|
||||
|
||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
||||
List<Holder<Biome>> b = new ArrayList<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
|
||||
.getValue(ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get());
|
||||
}
|
||||
} else {
|
||||
b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||
return getAllBiomes(
|
||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
||||
.lookup(Registries.BIOME).orElse(null),
|
||||
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
|
||||
engine).stream();
|
||||
}
|
||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId());
|
||||
Biome biome = customRegistry.getValue(resourceLocation);
|
||||
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
|
||||
if (optionalBiomeKey.isEmpty()) {
|
||||
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
|
||||
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(biomeKey);
|
||||
if (optionalReferenceHolder.isEmpty()) {
|
||||
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
m.put(j.getId(), optionalReferenceHolder.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
int m = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
||||
if (ib.isCustom()) {
|
||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
||||
} else {
|
||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
||||
return NMSBinding.biomeToBiomeBase(biomeRegistry, v);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R4;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.ResultLocator;
|
||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
addAll(dimension.getJigsawStructures(), placements);
|
||||
for (var region : dimension.getAllRegions(engine)) {
|
||||
addAll(region.getJigsawStructures(), placements);
|
||||
for (var biome : region.getAllBiomes(engine))
|
||||
addAll(biome.getJigsawStructures(), placements);
|
||||
}
|
||||
var stronghold = dimension.getStronghold();
|
||||
if (stronghold != null)
|
||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
||||
placements.removeIf(Objects::isNull);
|
||||
|
||||
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
|
||||
for (var s : placements) {
|
||||
try {
|
||||
String raw = s.getStructureKey();
|
||||
if (raw == null) continue;
|
||||
boolean tag = raw.startsWith("#");
|
||||
if (tag) raw = raw.substring(1);
|
||||
|
||||
var location = ResourceLocation.parse(raw);
|
||||
if (!tag) {
|
||||
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = TagKey.create(Registries.STRUCTURE, location);
|
||||
var set = registry.get(key).orElse(null);
|
||||
if (set == null) {
|
||||
Iris.error("Could not find structure tag: " + raw);
|
||||
continue;
|
||||
}
|
||||
for (var holder : set) {
|
||||
var resourceKey = holder.unwrapKey().orElse(null);
|
||||
if (resourceKey == null) continue;
|
||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
||||
if (placements == null) return;
|
||||
placements.stream()
|
||||
.map(IrisJigsawStructurePlacement::getStructure)
|
||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(structures::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
||||
if (holders.size() == 0) return null;
|
||||
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
|
||||
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
|
||||
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
|
||||
}
|
||||
if (engine.getDimension().isDisableExplorerMaps())
|
||||
return null;
|
||||
|
||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
||||
for (var holder : holders) {
|
||||
if (holder == null) continue;
|
||||
var key = holder.unwrapKey().orElse(null);
|
||||
var set = this.structures.get(key);
|
||||
if (set == null) continue;
|
||||
for (var structure : set) {
|
||||
structures.put(structure, holder);
|
||||
}
|
||||
}
|
||||
if (structures.isEmpty())
|
||||
return null;
|
||||
|
||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
||||
if (findUnexplored)
|
||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
||||
|
||||
try {
|
||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
||||
if (result == null) return null;
|
||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
||||
return Pair.of(blockPos, result.obj());
|
||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends ChunkGenerator> codec() {
|
||||
return MapCodec.unit(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator getDelegate() {
|
||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
||||
return chunkGenerator.getDelegate();
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return delegate.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return delegate.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
|
||||
return delegate.createState(holderlookup, randomstate, i, conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
||||
return delegate.getSpawnHeight(levelheightaccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return delegate.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
|
||||
return delegate.getTypeNameForDataFixer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
delegate.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
|
||||
return delegate.getBiomeGenerationSettings(holder);
|
||||
}
|
||||
|
||||
static {
|
||||
Field biomeSource = null;
|
||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
||||
if (!field.getType().equals(BiomeSource.class))
|
||||
continue;
|
||||
biomeSource = field;
|
||||
break;
|
||||
}
|
||||
if (biomeSource == null)
|
||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
try {
|
||||
BIOME_SOURCE.set(generator, source);
|
||||
if (generator instanceof CustomChunkGenerator custom)
|
||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
||||
|
||||
return generator;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,765 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R4;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.WorldLoader;
|
||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockStates;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
private final BlockData AIR = Material.AIR.createBlockData();
|
||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||
private Field biomeStorageCache = null;
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
||||
return type.getDeclaredClasses()[ordinal];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Material material) {
|
||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Location l) {
|
||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public KMap<String, Object> serializeTile(Location location) {
|
||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
||||
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
|
||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
||||
}
|
||||
|
||||
@Contract(value = "null, _, _ -> null", pure = true)
|
||||
private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) {
|
||||
if (tag == null || depth > maxDepth) return null;
|
||||
return switch (tag) {
|
||||
case CollectionTag collection -> {
|
||||
KList<Object> list = new KList<>();
|
||||
|
||||
for (Object i : collection) {
|
||||
if (i instanceof net.minecraft.nbt.Tag t)
|
||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||
else list.add(i);
|
||||
}
|
||||
yield list;
|
||||
}
|
||||
case net.minecraft.nbt.CompoundTag compound -> {
|
||||
KMap<String, Object> map = new KMap<>();
|
||||
|
||||
for (String key : compound.keySet()) {
|
||||
var child = compound.get(key);
|
||||
if (child == null) continue;
|
||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
||||
if (value == null) continue;
|
||||
map.put(key, value);
|
||||
}
|
||||
yield map;
|
||||
}
|
||||
case NumericTag numeric -> numeric.box();
|
||||
default -> tag.asString().orElse(null);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
J.s(() -> merge(level, blockPos, tag));
|
||||
}
|
||||
|
||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||
var blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||
var state = level.getBlockState(blockPos);
|
||||
if (!state.hasBlockEntity())
|
||||
return;
|
||||
|
||||
blockEntity = ((EntityBlock) state.getBlock())
|
||||
.newBlockEntity(blockPos, state);
|
||||
}
|
||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||
accessor.setData(accessor.getData().merge(tag));
|
||||
}
|
||||
|
||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
||||
return switch (object) {
|
||||
case Map<?, ?> map -> {
|
||||
var tag = new net.minecraft.nbt.CompoundTag();
|
||||
for (var i : map.entrySet()) {
|
||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case List<?> list -> {
|
||||
var tag = new net.minecraft.nbt.ListTag();
|
||||
for (var i : list) {
|
||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case Byte number -> ByteTag.valueOf(number);
|
||||
case Short number -> ShortTag.valueOf(number);
|
||||
case Integer number -> IntTag.valueOf(number);
|
||||
case Long number -> LongTag.valueOf(number);
|
||||
case Float number -> FloatTag.valueOf(number);
|
||||
case Double number -> DoubleTag.valueOf(number);
|
||||
case String string -> StringTag.valueOf(string);
|
||||
default -> EndTag.INSTANCE;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeEntity(Entity location) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomHeight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
||||
return registry().lookup(Registries.BIOME).orElse(null);
|
||||
}
|
||||
|
||||
private Registry<Block> getBlockRegistry() {
|
||||
return registry().lookup(Registries.BLOCK).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBaseFromId(int id) {
|
||||
return getCustomBiomeRegistry().get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight(World world) {
|
||||
return world.getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomBiomes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTrueBiomeBase(Location location) {
|
||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrueBiomeBaseKey(Location location) {
|
||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseFor(String mckey) {
|
||||
return getCustomBiomeRegistry().getValue(ResourceLocation.parse(mckey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
||||
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null);
|
||||
}
|
||||
|
||||
public int getBiomeBaseIdForKey(String key) {
|
||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyForBiomeBase(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(World world, Biome biome) {
|
||||
return biomeToBiomeBase(((CraftWorld) world).getHandle()
|
||||
.registryAccess().lookup(Registries.BIOME).orElse(null), biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(Object registry, Biome biome) {
|
||||
Object v = baseBiomeCache.get(biome);
|
||||
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
//noinspection unchecked
|
||||
v = biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
||||
if (v == null) {
|
||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
||||
// So, we will just return the ID that the plains biome returns instead.
|
||||
//noinspection unchecked
|
||||
return biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
||||
}
|
||||
baseBiomeCache.put(biome, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<Biome> getBiomes() {
|
||||
return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBukkit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiomeId(Biome biome) {
|
||||
for (World i : Bukkit.getWorlds()) {
|
||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
|
||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
||||
}
|
||||
}
|
||||
|
||||
return biome.ordinal();
|
||||
}
|
||||
|
||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
||||
return getCustomBiomeRegistry().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
||||
return getCustomBiomeRegistry().getId(paramT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
||||
return new MCABiomeContainer() {
|
||||
@Override
|
||||
public int[] getData() {
|
||||
return base.writeBiomes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int y, int z, int id) {
|
||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiome(int x, int y, int z) {
|
||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countCustomBiomes() {
|
||||
AtomicInteger a = new AtomicInteger(0);
|
||||
|
||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
||||
if (i.getNamespace().equals("minecraft")) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.incrementAndGet();
|
||||
Iris.debug("Custom Biome: " + i);
|
||||
});
|
||||
|
||||
return a.get();
|
||||
}
|
||||
|
||||
public boolean supportsDataPacks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
||||
c.markUnsaved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
||||
try {
|
||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
||||
s.setBiome(x, y, z, biome);
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Field getFieldForBiomeStorage(Object storage) {
|
||||
Field f = biomeStorageCache;
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
try {
|
||||
f = storage.getClass().getDeclaredField("biome");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.error(storage.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
biomeStorageCache = f;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCAPaletteAccess createPalette() {
|
||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
||||
cf.setAccessible(true);
|
||||
df.setAccessible(true);
|
||||
bf.setAccessible(true);
|
||||
net.minecraft.core.IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
||||
int b = bf.getInt(blockData);
|
||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||
return new MCAIdMapper<BlockState>(c, d, b);
|
||||
});
|
||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
((CraftBlockData) AIR).getState());
|
||||
return new MCAWrappedPalettedContainer<>(container,
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
||||
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
|
||||
AtomicInteger c = new AtomicInteger();
|
||||
AtomicInteger r = new AtomicInteger();
|
||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
||||
if (b != null) {
|
||||
if (b.isCustom()) {
|
||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get());
|
||||
c.getAndIncrement();
|
||||
} else {
|
||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
||||
r.getAndIncrement();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
||||
if (customNbt != null && !customNbt.isEmpty()) {
|
||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
||||
|
||||
try {
|
||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
|
||||
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe());
|
||||
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||
} catch (CommandSyntaxException var5) {
|
||||
throw new IllegalArgumentException(var5);
|
||||
}
|
||||
|
||||
return CraftItemStack.asBukkitCopy(s);
|
||||
} else {
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
|
||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||
worldGenContextField.setAccessible(true);
|
||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||
|
||||
var newContext = new WorldGenContext(
|
||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
|
||||
|
||||
worldGenContextField.set(chunkMap, newContext);
|
||||
}
|
||||
|
||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
||||
Field[] fields = EntityType.class.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
||||
try {
|
||||
EntityType entityType = (EntityType) field.get(null);
|
||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
||||
Vector<Float> v1 = new Vector<>();
|
||||
v1.add(entityType.getHeight());
|
||||
entityType.getDimensions();
|
||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
||||
return box;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.error("Unable to get entity dimensions!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.awt.Color getBiomeColor(Location location, BiomeColor type) {
|
||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
var biome = holder.value();
|
||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
||||
|
||||
int rgba = switch (type) {
|
||||
case FOG -> biome.getFogColor();
|
||||
case WATER -> biome.getWaterColor();
|
||||
case WATER_FOG -> biome.getWaterFogColor();
|
||||
case SKY -> biome.getSkyColor();
|
||||
case FOLIAGE -> biome.getFoliageColor();
|
||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
||||
};
|
||||
if (rgba == 0) {
|
||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
||||
return null;
|
||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
||||
return null;
|
||||
}
|
||||
return new Color(rgba, true);
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getType().equals(fieldType))
|
||||
return f;
|
||||
}
|
||||
throw new NoSuchFieldException(fieldType.getName());
|
||||
} catch (NoSuchFieldException var4) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass == null) {
|
||||
throw var4;
|
||||
} else {
|
||||
return getField(superClass, fieldType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
|
||||
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnChunkCount(World world) {
|
||||
var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS))
|
||||
.orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS));
|
||||
if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!");
|
||||
return (int) Math.pow(2 * radius + 1, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<String> getStructureKeys() {
|
||||
KList<String> keys = new KList<>();
|
||||
|
||||
var registry = registry().lookup(Registries.STRUCTURE).orElse(null);
|
||||
if (registry == null) return keys;
|
||||
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
||||
registry.getTags()
|
||||
.map(HolderSet.Named::key)
|
||||
.map(TagKey::location)
|
||||
.map(ResourceLocation::toString)
|
||||
.map(s -> "#" + s)
|
||||
.forEach(keys::add);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public AutoClosing injectLevelStems() {
|
||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||
|
||||
var server = ((CraftServer) Bukkit.getServer());
|
||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||
var nmsServer = server.getServer();
|
||||
var old = nmsServer.worldLoader;
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||
old.resources(),
|
||||
old.dataConfiguration(),
|
||||
old.datapackWorldgen(),
|
||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||
)));
|
||||
|
||||
return new AutoClosing(() -> {
|
||||
field.set(nmsServer, old);
|
||||
dataContextLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||
var reg = registry();
|
||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||
field.setAccessible(true);
|
||||
|
||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||
var fake = new HashMap<>(old);
|
||||
fake.put(Registries.LEVEL_STEM, injected);
|
||||
field.set(reg, fake);
|
||||
|
||||
return new AutoClosing(() -> field.set(reg, old));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||
return overworld || nether || end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCustomDimensions(World world) {
|
||||
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||
}
|
||||
|
||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||
var access = registry();
|
||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||
|
||||
var settings = new FlatLevelGeneratorSettings(
|
||||
Optional.empty(),
|
||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
||||
List.of()
|
||||
);
|
||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||
settings.updateLayers();
|
||||
|
||||
var source = new FlatLevelSource(settings);
|
||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||
|
||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
||||
|
||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||
}
|
||||
|
||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||
var loc = createIrisKey(key);
|
||||
target.register(key, new LevelStem(
|
||||
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||
source
|
||||
), RegistrationInfo.BUILT_IN);
|
||||
}
|
||||
|
||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||
if (source == null) return;
|
||||
source.listElementIds().forEach(key -> {
|
||||
var value = source.getValue(key);
|
||||
var info = source.registrationInfo(key).orElse(null);
|
||||
if (value != null && info != null && !target.containsKey(key))
|
||||
target.register(key, value, info);
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||
}
|
||||
}
|
@ -23,18 +23,19 @@ pluginManagement {
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id "org.gradle.toolchains.foojay-resolver-convention" version "0.8.0"
|
||||
id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
|
||||
rootProject.name = 'Iris'
|
||||
rootProject.name = "Iris"
|
||||
|
||||
include(':core')
|
||||
include(":core")
|
||||
include(
|
||||
':nms:v1_21_R3',
|
||||
':nms:v1_21_R2',
|
||||
':nms:v1_21_R1',
|
||||
':nms:v1_20_R4',
|
||||
':nms:v1_20_R3',
|
||||
':nms:v1_20_R2',
|
||||
':nms:v1_20_R1',
|
||||
":nms:v1_21_R4",
|
||||
":nms:v1_21_R3",
|
||||
":nms:v1_21_R2",
|
||||
":nms:v1_21_R1",
|
||||
":nms:v1_20_R4",
|
||||
":nms:v1_20_R3",
|
||||
":nms:v1_20_R2",
|
||||
":nms:v1_20_R1",
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user