Compare commits

...

102 Commits

Author SHA1 Message Date
Aidan Aeternum
840608a40f
v+ 2025-06-14 16:49:22 -04:00
Aidan Aeternum
fcedc35635
Merge pull request #1205 from VolmitSoftware/dev
3.6.11
2025-06-14 16:47:20 -04:00
Julian Krings
32d9a5e40a
make sentry engine context hotload safe 2025-06-14 11:46:51 +02:00
Julian Krings
8df6253604
add safeguard info to sentry reports 2025-06-14 11:44:30 +02:00
Julian Krings
01b62c13b6
fix deleting mantle temp files before they are fully written 2025-06-14 11:18:14 +02:00
Julian Krings
f32f73e65a
disable error reporting in dev environment 2025-06-13 12:27:03 +02:00
Julian Krings
7b7118fe0d
add id for servers hash of jvm name & version, processor info, memory size and plugins 2025-06-13 12:26:56 +02:00
Julian Krings
851ac18f0d
implement custom conditions for mythic mobs (#1204) 2025-06-11 13:23:09 +02:00
Julian Krings
37be7ca847
don't send json and zip file closed exceptions to sentry 2025-06-11 13:14:28 +02:00
Julian Krings
ed67b4d3c2
fix spawners not having entities due to using old format 2025-06-11 13:03:31 +02:00
Julian Krings
8712c8874c
disable global pregen cache by default for now 2025-06-09 23:27:12 +02:00
Julian Krings
ccc3bab8e0
allow carving recursion 2025-06-09 23:25:33 +02:00
Julian Krings
113f25dab8
more sentry context 2025-06-09 23:17:47 +02:00
Julian Krings
cd80acdc7d
fix IndexOutOfBoundsException when getting the selection from the wand 2025-06-09 19:50:50 +02:00
Julian Krings
9316ea9e5b
fix npe when creating cuboids 2025-06-09 19:49:06 +02:00
Julian Krings
e2a3f25dcb
fix load in parallel method 2025-06-09 19:42:03 +02:00
Julian Krings
944cc19ebc
use proper shuffling algorithm for the loot 2025-06-09 19:35:43 +02:00
Julian Krings
172e234514
fix divide by zero in the engine svc 2025-06-09 19:19:00 +02:00
Julian Krings
f9dac8a3a1
fix compile while missing the sentry auth token 2025-06-09 18:40:43 +02:00
Julian Krings
eefbbab7ee
remove legacy chars before minimessage deserialization 2025-06-09 18:39:11 +02:00
Julian Krings
9cf567e1ff
remove unused util to fix class not found exception 2025-06-09 13:47:24 +02:00
Julian Krings
b8ee7561dd
fix failing to create temp file when user deleted the temp directory 2025-06-09 13:44:57 +02:00
Julian Krings
c0d17742e8
fix release task for sentry 2025-06-09 13:43:00 +02:00
Julian Krings
a88d389e0f
bump gradle wrapper to 8.14.2 and switch to kotlin dsl (#1203) 2025-06-09 12:45:27 +02:00
Julian Krings
b341089996
v+ 2025-06-09 02:12:14 +02:00
Julian Krings
9cbfd5a10b
Merge pull request #1202 from VolmitSoftware/dev
3.6.10
2025-06-09 02:11:22 +02:00
Julian Krings
fdaf8ff9d3
relocate sentry to fix incompatibility with some plugins using a very old sentry version 2025-06-09 00:17:38 +02:00
Julian Krings
e63d84c052
remove server name from reports 2025-06-09 00:16:58 +02:00
Julian Krings
0d103a934a
fix ticking engine players in the wrong dimension 2025-06-08 12:43:56 +02:00
Julian Krings
8119207254
sentry changes
- info message on enabling
- add release tag
- disable uncaught exception handling to prevent reporting other plugins issues
2025-06-08 12:29:24 +02:00
Julian Krings
b66e6d8335
correct sentry dsn 2025-06-07 14:00:22 +02:00
Julian Krings
ce2b62f5ae
add sentry release task 2025-06-06 18:37:11 +02:00
Julian Krings
c767b6c8e8
check sentry env token as fallback 2025-06-06 18:36:41 +02:00
Julian Krings
67328d7d10
remove test exception for sentry 2025-06-06 17:35:13 +02:00
Julian Krings
329e136a66
implement opt-out auto reporting with sentry (#1201) 2025-06-06 17:33:16 +02:00
Julian Krings
52f87befa2
return noop pregen cache when the service is disabled and write pregen cache to temp file first then replace real one 2025-06-06 17:24:23 +02:00
Julian Krings
2ee22db072
fix mantle write failing on windows 2025-06-06 16:49:42 +02:00
Julian Krings
5705caa1ba
fix benchmarking not disabling properly 2025-06-04 20:20:48 +02:00
Julian Krings
a56cd4c268
use block data use for slice 2025-06-04 16:32:29 +02:00
Julian Krings
48cc6bb49c
improve mantle writer speeds 2025-06-04 15:03:00 +02:00
Aidan Aeternum
635ee02459
v+ 2025-06-02 18:48:01 -04:00
Aidan Aeternum
2c7b7c8c91
Merge pull request #1199 from VolmitSoftware/dev
3.8.9
2025-06-02 18:47:38 -04:00
Julian Krings
ad0662be54
initial 1.21.5 bindings 2025-06-02 18:18:17 +02:00
Julian Krings
835c8422f1
calculate spawn location async 2025-06-02 17:42:36 +02:00
Julian Krings
2c60192de3
use scheduled thread pool instead of loopers for the EngineSVC 2025-06-02 17:29:38 +02:00
Julian Krings
adb7188eb9
refactor mantle cleanup 2025-06-02 17:29:30 +02:00
Julian Krings
2436ebb857
Merge pull request #1198 from VolmitSoftware/feat/versioned_mantle 2025-06-02 17:28:58 +02:00
Julian Krings
ad85a0bbd1
implement marker exhaustionChance 2025-05-29 23:17:23 +02:00
Julian Krings
22ac9ebf47
update nexo api to release 1.6.0 2025-05-29 14:18:30 +02:00
Julian Krings
3cb9585dd8
fix pregenerator not closing when a new one is started 2025-05-28 21:48:41 +02:00
Julian Krings
f4d1177c51
fix cave fluid ignoring fluid palette 2025-05-28 12:57:11 +02:00
Julian Krings
b132862a60
update last use for in use tectonic plates 2025-05-27 22:50:30 +02:00
Julian Krings
2452a2f633
cleanup of the mantle trimmer / EngineSVC 2025-05-27 16:23:02 +02:00
Julian Krings
f0476fea9b
implement version header for tectonic plates 2025-05-26 20:54:12 +02:00
Julian Krings
90c6457d37 write to plates to a temp file first and then move it into the mantle dir 2025-05-26 17:29:15 +02:00
Julian Krings
61301ffd4d close engines using a shutdown hook 2025-05-26 17:29:15 +02:00
Julian Krings
e42317139d fix engines not closing on server stop 2025-05-26 17:29:15 +02:00
Julian Krings
97ddfd309b
fix updater not working 2025-05-21 13:05:07 +02:00
Aidan Aeternum
5d42c5cae0
v+ 2025-05-20 12:32:04 -04:00
Aidan Aeternum
52fecb48d8
Merge pull request #1196 from VolmitSoftware/dev
3.6.8
2025-05-20 12:31:35 -04:00
Julian Krings
ce29dc98c3 fix spawn markers not being removed 2025-05-16 12:41:31 +02:00
Julian Krings
463e3d9658
Merge pull request #1193 from VolmitSoftware/feat/faster_pregen
Feat/faster pregen
2025-05-16 12:29:41 +02:00
Julian Krings
9152b25d51 fix pack hash calculation 2025-05-16 12:22:35 +02:00
Julian Krings
bb9c72e414
Partially Merge pull request #1192 from dan28000/Iris 2025-05-16 12:11:46 +02:00
Julian Krings
976648340e
add pregen method that doesn't use waiting threads 2025-05-16 12:02:38 +02:00
Julian Krings
49ce84a9e9
Merge branch 'dev' into feat/faster_pregen
# Conflicts:
#	core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java
2025-04-27 13:56:28 +02:00
Aidan Aeternum
f93995e152
v+ 2025-04-21 20:27:43 -04:00
Aidan Aeternum
7a5a2e5909
Merge pull request #1191 from VolmitSoftware/dev
3.6.7
2025-04-21 20:27:18 -04:00
dan28000
e15d31a0ed would be nice to display the right number 2025-04-21 22:59:29 +02:00
dan28000
09cdd61a68 fix method
when having relocated worlds folder this method would fail making warning appear
2025-04-21 20:45:20 +02:00
dan28000
0575cd85c8 delete old stuff
delete legacy command
2025-04-21 20:44:38 +02:00
Pixel
f1c72974fd
Cleanup (#1186)
* Remove server benchmark command

* Moved lazy pregen to dev
2025-04-15 15:53:50 +02:00
Julian Krings
26e2e20840
Feat/pregen cache (#1190)
Add cache to pregen to skip already generated chunks faster
2025-04-14 21:26:39 +02:00
Julian Krings
c00dcf205b fix deadlock when closing pregen method while using modified concurrency 2025-04-14 21:25:31 +02:00
Julian Krings
a10a784c3b add max concurrency setting for pregen 2025-04-14 21:25:31 +02:00
Julian Krings
f99cc61042 add setting to use a virtual thread executor instead of MultiBurst for async pregen 2025-04-14 21:25:31 +02:00
Julian Krings
d2a1e5cc1e fix duplicate & redundant purpur recommendations 2025-04-14 21:23:52 +02:00
Julian Krings
3e2c0fa025 Decrease MSPT impact of throwing ender eyes in Iris worlds 2025-04-14 21:23:37 +02:00
Aidan Aeternum
ab4770400e
v+ 2025-04-10 04:49:32 -04:00
Aidan Aeternum
1a810d5d62
Merge pull request #1184 from VolmitSoftware/dev
3.6.6
2025-04-10 04:49:02 -04:00
Julian Krings
536d20bca7
remove context injection on world init 2025-04-09 12:16:03 +02:00
Julian Krings
9a691ac5b4
setup runServer for all supported versions 2025-04-09 10:57:35 +02:00
Julian Krings
71078a20a9
Don't inject world load context for main worlds to prevent incompatibilities 2025-04-08 21:15:19 +02:00
Julian Krings
566fca2b52
Add info message for failed increase of worker threads 2025-04-07 19:00:06 +02:00
Julian Krings
74e2576ca2
Prevent saving the iris level stems 2025-04-06 16:31:35 +02:00
Julian Krings
fb0bc112e3
increase worker threads on paper servers during pregen 2025-03-30 14:31:11 +02:00
Julian Krings
407e51378c fix applying x offset to z coords in Spiraler 2025-03-27 15:02:47 +01:00
Julian Krings
c468eb1ab1 make pregen use block radius as input 2025-03-27 15:02:47 +01:00
Aidan Aeternum
bdb7cc61e5
v+ 2025-03-26 15:41:35 -04:00
Aidan Aeternum
e8f9e841c4
Merge pull request #1181 from VolmitSoftware/dev
3.6.5
2025-03-26 15:41:06 -04:00
Julian Krings
1b1b9d97b7
update overworld pack to 31020 2025-03-25 19:15:47 +01:00
Julian Krings
24355064ff add safeguard for missing dimension types to prevent world corruption 2025-03-25 19:14:20 +01:00
Julian Krings
06a45056d9 use flat level source instead of trying to get the levelstems 2025-03-22 12:38:25 +01:00
Aidan Aeternum
dfe4894be7
v+ 2025-03-18 16:40:33 -04:00
Aidan Aeternum
8eb2287ec0
Merge pull request #1178 from VolmitSoftware/dev
3.6.4
2025-03-18 16:34:22 -04:00
Julian Krings
c4f0722614 improve datapack setup speed 2025-03-18 16:18:37 +01:00
Julian Krings
7fa1484b21 fix levelstem injection not working for main worlds 2025-03-18 16:18:37 +01:00
Julian Krings
1c5eb8b910 automatically update vanilla dimension type if present in Iris datapack 2025-03-18 16:18:37 +01:00
Julian Krings
94bf530d93 add setting to enable changing to the vanilla dimension height values 2025-03-18 16:18:37 +01:00
Julian Krings
686ae57b5b switch from world preset to direct level stems 2025-03-18 16:18:37 +01:00
Julian Krings
a911685aaf Fix world gen datapack incompatibilities 2025-03-18 16:18:37 +01:00
Aidan Aeternum
6899761ca9
v+ 2025-03-06 17:06:34 -05:00
116 changed files with 6094 additions and 4357 deletions

View File

@ -1,254 +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/>.
*/
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"
}
version '3.6.2-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 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.of()
NMS_BINDINGS.each { nms ->
project(":nms:${nms.key}") {
apply plugin: 'java'
apply plugin: 'com.volmit.nmstools'
nmsTools {
it.jvm = JVM_VERSION.getOrDefault(nms.key, 21)
it.version = nms.value
}
dependencies {
implementation project(":core")
}
}
}
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 'bsf:bsf:2.4.0'
compileOnly 'rhino:js:1.7R2'
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'
}
/**
* 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
View 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" }
}
}

View File

@ -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()
)
}
}

151
core/build.gradle.kts Normal file
View File

@ -0,0 +1,151 @@
/*
* 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("bsf:bsf:2.4.0")
compileOnly("rhino:js:1.7R2")
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")
}
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()
}

View File

@ -30,11 +30,12 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.safeguard.ServerBootSFG;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.core.tools.IrisWorldCreator;
import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.EnginePanic;
import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.engine.object.IrisCompat;
import com.volmit.iris.engine.object.IrisContextInjector;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.object.IrisWorld;
import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.engine.platform.BukkitChunkGenerator;
@ -44,6 +45,7 @@ import com.volmit.iris.core.safeguard.UtilsSFG;
import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.exceptions.IrisException; import com.volmit.iris.util.exceptions.IrisException;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
@ -52,6 +54,7 @@ import com.volmit.iris.util.io.FileWatcher;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.io.InstanceState; import com.volmit.iris.util.io.InstanceState;
import com.volmit.iris.util.io.JarScanner; import com.volmit.iris.util.io.JarScanner;
import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.misc.getHardware;
@ -63,7 +66,11 @@ import com.volmit.iris.util.reflect.ShadeFix;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.Queue;
import com.volmit.iris.util.scheduling.ShurikenQueue; import com.volmit.iris.util.scheduling.ShurikenQueue;
import com.volmit.iris.util.sentry.Attachments;
import com.volmit.iris.util.sentry.IrisLogger;
import com.volmit.iris.util.sentry.ServerID;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import io.sentry.Sentry;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.ComponentSerializer;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
@ -391,6 +398,7 @@ public class Iris extends VolmitPlugin implements Listener {
} }
public static void reportError(Throwable e) { public static void reportError(Throwable e) {
Sentry.captureException(e);
if (IrisSettings.get().getGeneral().isDebug()) { if (IrisSettings.get().getGeneral().isDebug()) {
String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber();
@ -455,12 +463,16 @@ public class Iris extends VolmitPlugin implements Listener {
instance = this; instance = this;
services = new KMap<>(); services = new KMap<>();
setupAudience(); setupAudience();
setupSentry();
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i)); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
INMS.get(); INMS.get();
IO.delete(new File("iris")); IO.delete(new File("iris"));
compat = IrisCompat.configured(getDataFile("compat.json"));
ServerConfigurator.configure();
new IrisContextInjector();
IrisSafeguard.IrisSafeguardSystem(); IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag()); getSender().setTag(getTag());
compat = IrisCompat.configured(getDataFile("compat.json")); IrisSafeguard.earlySplash();
linkMultiverseCore = new MultiverseCoreLink(); linkMultiverseCore = new MultiverseCoreLink();
linkMythicMobs = new MythicMobsLink(); linkMythicMobs = new MythicMobsLink();
configWatcher = new FileWatcher(getDataFile("settings.json")); configWatcher = new FileWatcher(getDataFile("settings.json"));
@ -515,15 +527,15 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.info("Loading World: %s | Generator: %s", s, generator); Iris.info("Loading World: %s | Generator: %s", s, generator);
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
new WorldCreator(s) WorldCreator c = new WorldCreator(s)
.type(IrisWorldCreator.IRIS)
.generator(getDefaultWorldGenerator(s, generator)) .generator(getDefaultWorldGenerator(s, generator))
.environment(IrisData.loadAnyDimension(generator).getEnvironment()) .environment(IrisData.loadAnyDimension(generator).getEnvironment());
.createWorld(); INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
reportError(e);
} }
} }
@ -541,7 +553,7 @@ public class Iris extends VolmitPlugin implements Listener {
}); });
}); });
} catch (IrisException e) { } catch (IrisException e) {
e.printStackTrace(); reportError(e);
} }
} }
} }
@ -573,9 +585,19 @@ public class Iris extends VolmitPlugin implements Listener {
Bukkit.getScheduler().cancelTasks(this); Bukkit.getScheduler().cancelTasks(this);
HandlerList.unregisterAll((Plugin) this); HandlerList.unregisterAll((Plugin) this);
postShutdown.forEach(Runnable::run); postShutdown.forEach(Runnable::run);
services.clear();
MultiBurst.burst.close();
super.onDisable(); super.onDisable();
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
Bukkit.getWorlds()
.stream()
.map(IrisToolbelt::access)
.filter(Objects::nonNull)
.forEach(PlatformChunkGenerator::close);
MultiBurst.burst.close();
services.clear();
}));
} }
private void setupPapi() { private void setupPapi() {
@ -621,12 +643,22 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.warn("="); Iris.warn("=");
Iris.warn("============================================"); Iris.warn("============================================");
} }
if (!instance.getServer().getVersion().contains("Purpur")) {
passed = false; try {
Class.forName("io.papermc.paper.configuration.PaperConfigurations");
} catch (ClassNotFoundException e) {
Iris.info(C.RED + "Iris requires paper or above to function properly..");
return false;
}
try {
Class.forName("org.purpurmc.purpur.PurpurConfig");
} catch (ClassNotFoundException e) {
Iris.info("We recommend using Purpur for the best experience with Iris."); Iris.info("We recommend using Purpur for the best experience with Iris.");
Iris.info("Purpur is a fork of Paper that is optimized for performance and stability."); Iris.info("Purpur is a fork of Paper that is optimized for performance and stability.");
Iris.info("Plugins that work on Spigot / Paper work on Purpur."); Iris.info("Plugins that work on Spigot / Paper work on Purpur.");
Iris.info("You can download it here: https://purpurmc.org"); Iris.info("You can download it here: https://purpurmc.org");
return false;
} }
return passed; return passed;
} }
@ -851,13 +883,6 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); } } else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
Iris.info("Java: " + getJava()); Iris.info("Java: " + getJava());
if (!instance.getServer().getVersion().contains("Purpur")) {
if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) {
Iris.info(C.RED + " Iris requires paper or above to function properly..");
} else {
Iris.info(C.YELLOW + "Purpur is recommended to use with iris.");
}
}
if (getHardware.getProcessMemory() < 5999) { if (getHardware.getProcessMemory() < 5999) {
Iris.warn("6GB+ Ram is recommended"); Iris.warn("6GB+ Ram is recommended");
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB"); Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
@ -924,4 +949,43 @@ public class Iris extends VolmitPlugin implements Listener {
return -1; return -1;
} }
} }
private static boolean suppress(Throwable e) {
return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException;
}
private static void setupSentry() {
var settings = IrisSettings.get().getSentry();
if (settings.disableAutoReporting || Sentry.isEnabled() || !Boolean.getBoolean("iris.errorReporting")) return;
Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings.");
Iris.info("Your server ID is: " + ServerID.ID);
Sentry.init(options -> {
options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904");
if (settings.debug) {
options.setLogger(new IrisLogger());
options.setDebug(true);
}
options.setAttachServerName(false);
options.setEnableUncaughtExceptionHandler(false);
options.setRelease(Iris.instance.getDescription().getVersion());
options.setBeforeSend((event, hint) -> {
if (suppress(event.getThrowable())) return null;
event.setTag("iris.safeguard", IrisSafeguard.mode());
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
var context = IrisContext.get();
if (context != null) event.getContexts().set("engine", context.asContext());
event.getContexts().set("safeguard", ServerBootSFG.allIncompatibilities);
return event;
});
});
Sentry.configureScope(scope -> {
if (settings.includeServerId) scope.setUser(ServerID.asUser());
scope.addAttachment(Attachments.PLUGINS);
scope.setTag("server", Bukkit.getVersion());
scope.setTag("server.type", Bukkit.getName());
scope.setTag("server.api", Bukkit.getBukkitVersion());
});
Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close));
}
} }

View File

@ -23,8 +23,8 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.ChronoLatch;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -45,6 +45,8 @@ public class IrisSettings {
private IrisSettingsStudio studio = new IrisSettingsStudio(); private IrisSettingsStudio studio = new IrisSettingsStudio();
private IrisSettingsPerformance performance = new IrisSettingsPerformance(); private IrisSettingsPerformance performance = new IrisSettingsPerformance();
private IrisSettingsUpdater updater = new IrisSettingsUpdater(); private IrisSettingsUpdater updater = new IrisSettingsUpdater();
private IrisSettingsPregen pregen = new IrisSettingsPregen();
private IrisSettingsSentry sentry = new IrisSettingsSentry();
public static int getThreadCount(int c) { public static int getThreadCount(int c) {
return switch (c) { return switch (c) {
@ -130,21 +132,46 @@ public class IrisSettings {
public boolean markerEntitySpawningSystem = true; public boolean markerEntitySpawningSystem = true;
public boolean effectSystem = true; public boolean effectSystem = true;
public boolean worldEditWandCUI = true; public boolean worldEditWandCUI = true;
public boolean globalPregenCache = false;
} }
@Data @Data
public static class IrisSettingsConcurrency { public static class IrisSettingsConcurrency {
public int parallelism = -1; 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 @Data
public static class IrisSettingsPerformance { public static class IrisSettingsPerformance {
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
public boolean trimMantleInStudio = false; public boolean trimMantleInStudio = false;
public int mantleKeepAlive = 30; public int mantleKeepAlive = 30;
public int cacheSize = 4_096; public int cacheSize = 4_096;
public int resourceLoaderCacheSize = 1_024; public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096; public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512; 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 @Data
@ -176,11 +203,13 @@ public class IrisSettings {
public boolean DoomsdayAnnihilationSelfDestructMode = false; public boolean DoomsdayAnnihilationSelfDestructMode = false;
public boolean commandSounds = true; public boolean commandSounds = true;
public boolean debug = false; public boolean debug = false;
public boolean dumpMantleOnError = false;
public boolean disableNMS = false; public boolean disableNMS = false;
public boolean pluginMetrics = true; public boolean pluginMetrics = true;
public boolean splashLogoStartup = true; public boolean splashLogoStartup = true;
public boolean useConsoleCustomColors = true; public boolean useConsoleCustomColors = true;
public boolean useCustomColorsIngame = true; public boolean useCustomColorsIngame = true;
public boolean adjustVanillaHeight = false;
public String forceMainWorld = ""; public String forceMainWorld = "";
public int spinh = -20; public int spinh = -20;
public int spins = 7; public int spins = 7;
@ -194,6 +223,13 @@ public class IrisSettings {
} }
} }
@Data
public static class IrisSettingsSentry {
public boolean includeServerId = true;
public boolean disableAutoReporting = false;
public boolean debug = false;
}
@Data @Data
public static class IrisSettingsGUI { public static class IrisSettingsGUI {
public boolean useServerLaunchedGuis = true; public boolean useServerLaunchedGuis = true;
@ -215,4 +251,14 @@ public class IrisSettings {
public boolean disableTimeAndWeather = true; public boolean disableTimeAndWeather = true;
public boolean autoStartDefaultStudio = false; 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);
}
}
} }

View File

@ -28,21 +28,24 @@ import com.volmit.iris.engine.object.IrisBiomeCustom;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisRange; import com.volmit.iris.engine.object.IrisRange;
import com.volmit.iris.util.collection.KList; 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.collection.KSet;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.misc.ServerProperties;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.Data; import lombok.Data;
import lombok.NonNull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -68,10 +71,12 @@ public class ServerConfigurator {
f.load(spigotConfig); f.load(spigotConfig);
long tt = f.getLong("settings.timeout-time"); long tt = f.getLong("settings.timeout-time");
if (tt < TimeUnit.MINUTES.toSeconds(5)) { long spigotTimeout = TimeUnit.MINUTES.toSeconds(5);
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)");
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."); 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); f.save(spigotConfig);
} }
} }
@ -81,20 +86,22 @@ public class ServerConfigurator {
f.load(spigotConfig); f.load(spigotConfig);
long tt = f.getLong("watchdog.early-warning-delay"); long tt = f.getLong("watchdog.early-warning-delay");
if (tt < TimeUnit.MINUTES.toMillis(3)) { long watchdog = TimeUnit.MINUTES.toMillis(3);
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)"); 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."); 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); f.save(spigotConfig);
} }
} }
private static List<File> getDatapacksFolder() { private static KList<File> getDatapacksFolder() {
if (!IrisSettings.get().getGeneral().forceMainWorld.isEmpty()) { if (!IrisSettings.get().getGeneral().forceMainWorld.isEmpty()) {
return new KList<File>().qadd(new File(Bukkit.getWorldContainer(), IrisSettings.get().getGeneral().forceMainWorld + "/datapacks")); return new KList<File>().qadd(new File(Bukkit.getWorldContainer(), IrisSettings.get().getGeneral().forceMainWorld + "/datapacks"));
} }
KList<File> worlds = new KList<>(); KList<File> worlds = new KList<>();
Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks"))); Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks")));
if (worlds.isEmpty()) worlds.add(new File(Bukkit.getWorldContainer(), ServerProperties.LEVEL_NAME + "/datapacks"));
return worlds; return worlds;
} }
@ -105,14 +112,16 @@ public class ServerConfigurator {
public static void installDataPacks(IDataFixer fixer, boolean fullInstall) { public static void installDataPacks(IDataFixer fixer, boolean fullInstall) {
Iris.info("Checking Data Packs..."); Iris.info("Checking Data Packs...");
DimensionHeight height = new DimensionHeight(fixer); DimensionHeight height = new DimensionHeight(fixer);
KList<File> folders = getDatapacksFolder();
KMap<String, KSet<String>> biomes = new KMap<>();
allPacks().flatMap(height::merge) allPacks().flatMap(height::merge)
.parallel()
.forEach(dim -> { .forEach(dim -> {
for (File dpack : getDatapacksFolder()) {
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
dim.installDataPack(fixer, dim::getLoader, dpack, height); dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
}
}); });
IrisDimension.writeShared(folders, height);
Iris.info("Data Packs Setup!"); Iris.info("Data Packs Setup!");
@ -164,7 +173,7 @@ public class ServerConfigurator {
Iris.warn("This will only happen when your pack changes (updates/first time setup)"); Iris.warn("This will only happen when your pack changes (updates/first time setup)");
Iris.warn("(You can disable this auto restart in iris settings)"); Iris.warn("(You can disable this auto restart in iris settings)");
J.s(() -> { J.s(() -> {
Iris.warn("Looks like the restart command diddn't work. Stopping the server instead!"); Iris.warn("Looks like the restart command didn't work. Stopping the server instead!");
Bukkit.shutdown(); Bukkit.shutdown();
}, 100); }, 100);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart"); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
@ -172,22 +181,24 @@ public class ServerConfigurator {
} }
public static boolean verifyDataPackInstalled(IrisDimension dimension) { public static boolean verifyDataPackInstalled(IrisDimension dimension) {
IrisData idm = IrisData.get(Iris.instance.getDataFolder("packs", dimension.getLoadKey()));
KSet<String> keys = new KSet<>(); KSet<String> keys = new KSet<>();
boolean warn = false; boolean warn = false;
for (IrisBiome i : dimension.getAllBiomes(() -> idm)) { for (IrisBiome i : dimension.getAllBiomes(dimension::getLoader)) {
if (i.isCustom()) { if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) { for (IrisBiomeCustom j : i.getCustomDerivitives()) {
keys.add(dimension.getLoadKey() + ":" + j.getId()); keys.add(dimension.getLoadKey() + ":" + j.getId());
} }
} }
} }
String key = getWorld(dimension.getLoader());
if (key == null) key = dimension.getLoadKey();
else key += "/" + dimension.getLoadKey();
if (!INMS.get().supportsDataPacks()) { if (!INMS.get().supportsDataPacks()) {
if (!keys.isEmpty()) { if (!keys.isEmpty()) {
Iris.warn("==================================================================================="); Iris.warn("===================================================================================");
Iris.warn("Pack " + dimension.getLoadKey() + " has " + keys.size() + " custom biome(s). "); Iris.warn("Pack " + key + " has " + keys.size() + " custom biome(s). ");
Iris.warn("Your server version does not yet support datapacks for iris."); Iris.warn("Your server version does not yet support datapacks for iris.");
Iris.warn("The world will generate these biomes as backup biomes."); Iris.warn("The world will generate these biomes as backup biomes.");
Iris.warn("===================================================================================="); Iris.warn("====================================================================================");
@ -206,7 +217,7 @@ public class ServerConfigurator {
} }
if (warn) { if (warn) {
Iris.error("The Pack " + dimension.getLoadKey() + " is INCAPABLE of generating custom biomes"); Iris.error("The Pack " + key + " is INCAPABLE of generating custom biomes");
Iris.error("If not done automatically, restart your server before generating with this pack!"); Iris.error("If not done automatically, restart your server before generating with this pack!");
} }
@ -220,6 +231,17 @@ public class ServerConfigurator {
.map(IrisData::get); .map(IrisData::get);
} }
@Nullable
public static String getWorld(@NonNull IrisData data) {
String worldContainer = Bukkit.getWorldContainer().getAbsolutePath();
if (!worldContainer.endsWith(File.separator)) worldContainer += File.separator;
String path = data.getDataFolder().getAbsolutePath();
if (!path.startsWith(worldContainer)) return null;
int l = path.endsWith(File.separator) ? 11 : 10;
return path.substring(worldContainer.length(), path.length() - l);
}
private static Stream<File> listFiles(File parent) { private static Stream<File> listFiles(File parent) {
var files = parent.listFiles(); var files = parent.listFiles();
return files == null ? Stream.empty() : Arrays.stream(files); return files == null ? Stream.empty() : Arrays.stream(files);

View File

@ -1,134 +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.pregenerator.DeepSearchPregenerator;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.TurboPregenerator;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.data.Dimension;
import com.volmit.iris.util.decree.DecreeExecutor;
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.math.Position2;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
@Decree(name = "DeepSearch", aliases = "search", description = "Pregenerate your Iris worlds!")
public class CommandDeepSearch implements DecreeExecutor {
public String worldName;
@Decree(description = "DeepSearch a world")
public void start(
@Param(description = "The radius of the pregen in blocks", aliases = "size")
int radius,
@Param(description = "The world to pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
Vector center
) {
worldName = world.getName();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "DeepSearch.json");
if (TurboFile.exists()) {
if (DeepSearchPregenerator.getInstance() != null) {
sender().sendMessage(C.BLUE + "DeepSearch is already in progress");
Iris.info(C.YELLOW + "DeepSearch is already in progress");
return;
} else {
try {
TurboFile.delete();
} catch (Exception e){
Iris.error("Failed to delete the old instance file of DeepSearch!");
return;
}
}
}
try {
if (sender().isPlayer() && access() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null!");
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
}
DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder()
.world(world)
.radiusBlocks(radius)
.position(0)
.build();
File SearchGenFile = new File(worldDirectory, "DeepSearch.json");
DeepSearchPregenerator pregenerator = new DeepSearchPregenerator(DeepSearchJob, SearchGenFile);
pregenerator.start();
String msg = C.GREEN + "DeepSearch started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
sender().sendMessage(msg);
Iris.info(msg);
} catch (Throwable e) {
sender().sendMessage(C.RED + "Epic fail. See console.");
Iris.reportError(e);
e.printStackTrace();
}
}
@Decree(description = "Stop the active DeepSearch task", aliases = "x")
public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException {
DeepSearchPregenerator DeepSearchInstance = DeepSearchPregenerator.getInstance();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File turboFile = new File(worldDirectory, "DeepSearch.json");
if (DeepSearchInstance != null) {
DeepSearchInstance.shutdownInstance(world);
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists() && turboFile.delete()) {
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists()) {
Iris.error("Failed to delete the old instance file of Turbo Pregen!");
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
}
}
@Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
public void pause(
@Param(aliases = "world", description = "The world to pause")
World world
) {
if (TurboPregenerator.getInstance() != null) {
TurboPregenerator.setPausedTurbo(world);
sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + ".");
} else {
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "DeepSearch.json");
if (TurboFile.exists()){
TurboPregenerator.loadTurboGenerator(world.getName());
sender().sendMessage(C.YELLOW + "Started DeepSearch back up!");
} else {
sender().sendMessage(C.YELLOW + "No active DeepSearch tasks to pause/unpause.");
}
}
}
}

View File

@ -20,13 +20,13 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.ServerConfigurator; 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.nms.datapack.DataVersion;
import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension; 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.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.DecreeOrigin;
import com.volmit.iris.util.decree.annotations.Decree; import com.volmit.iris.util.decree.annotations.Decree;
@ -36,6 +36,7 @@ import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate; import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.nbt.mca.MCAFile; import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
@ -62,57 +63,56 @@ import java.util.zip.GZIPOutputStream;
@Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"}) @Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
public class CommandDeveloper implements DecreeExecutor { public class CommandDeveloper implements DecreeExecutor {
private CommandTurboPregen turboPregen; private CommandTurboPregen turboPregen;
private CommandLazyPregen lazyPregen;
private CommandUpdater updater; private CommandUpdater updater;
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
public void EngineStatus() { public void EngineStatus() {
List<World> IrisWorlds = new ArrayList<>(); Iris.service(IrisEngineSVC.class)
int TotalLoadedChunks = 0; .engineStatus(sender());
int TotalQueuedTectonicPlates = 0; }
int TotalNotQueuedTectonicPlates = 0;
int TotalTectonicPlates = 0;
long lowestUnloadDuration = 0; @Decree(description = "Send a test exception to sentry")
long highestUnloadDuration = 0; public void Sentry() {
Engine engine = engine();
if (engine != null) IrisContext.getOr(engine);
Iris.reportError(new Exception("This is a test"));
}
for (World world : Bukkit.getWorlds()) { @Decree(description = "Test")
public void dumpThreads() {
try { try {
if (IrisToolbelt.access(world).getEngine() != null) { File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt");
IrisWorlds.add(world); FileOutputStream fos = new FileOutputStream(fi);
} Map<Thread, StackTraceElement[]> f = Thread.getAllStackTraces();
} catch (Exception e) { PrintWriter pw = new PrintWriter(fos);
// no
} pw.println(Thread.activeCount() + "/" + f.size());
var run = Runtime.getRuntime();
pw.println("Memory:");
pw.println("\tMax: " + run.maxMemory());
pw.println("\tTotal: " + run.totalMemory());
pw.println("\tFree: " + run.freeMemory());
pw.println("\tUsed: " + (run.totalMemory() - run.freeMemory()));
for (Thread i : f.keySet()) {
pw.println("========================================");
pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name());
for (StackTraceElement j : f.get(i)) {
pw.println(" @ " + j.toString());
} }
for (World world : IrisWorlds) { pw.println("========================================");
Engine engine = IrisToolbelt.access(world).getEngine(); pw.println();
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload(); pw.println();
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(); pw.close();
Iris.info("DUMPED! See " + fi.getAbsolutePath());
} catch (Throwable e) {
e.printStackTrace();
} }
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 = "Test") @Decree(description = "Test")
@ -128,7 +128,7 @@ public class CommandDeveloper implements DecreeExecutor {
File tectonicplates = new File(folder, "mantle"); File tectonicplates = new File(folder, "mantle");
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) { for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
TectonicPlate.read(maxHeight, i); TectonicPlate.read(maxHeight, i, true);
c++; c++;
Iris.info("Loaded count: " + c ); Iris.info("Loaded count: " + c );
@ -140,7 +140,7 @@ public class CommandDeveloper implements DecreeExecutor {
public void packBenchmark( public void packBenchmark(
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld") @Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
IrisDimension dimension, IrisDimension dimension,
@Param(description = "Radius in regions", defaultValue = "5") @Param(description = "Radius in regions", defaultValue = "2048")
int radius, int radius,
@Param(description = "Open GUI while benchmarking", defaultValue = "false") @Param(description = "Open GUI while benchmarking", defaultValue = "false")
boolean gui boolean gui
@ -234,7 +234,8 @@ public class CommandDeveloper implements DecreeExecutor {
@Param(description = "base IrisWorld") World world, @Param(description = "base IrisWorld") World world,
@Param(description = "raw TectonicPlate File") String path, @Param(description = "raw TectonicPlate File") String path,
@Param(description = "Algorithm to Test") String algorithm, @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)) { 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())); 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; return;
@ -251,7 +252,7 @@ public class CommandDeveloper implements DecreeExecutor {
service.submit(() -> { service.submit(() -> {
try { try {
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file)); CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
TectonicPlate plate = new TectonicPlate(height, raw); TectonicPlate plate = new TectonicPlate(height, raw, versioned);
raw.close(); raw.close();
double d1 = 0; double d1 = 0;
@ -270,7 +271,7 @@ public class CommandDeveloper implements DecreeExecutor {
size = tmp.length(); size = tmp.length();
start = System.currentTimeMillis(); start = System.currentTimeMillis();
CountingDataInputStream din = createInput(tmp, algorithm); CountingDataInputStream din = createInput(tmp, algorithm);
new TectonicPlate(height, din); new TectonicPlate(height, din, true);
din.close(); din.close();
d2 += System.currentTimeMillis() - start; d2 += System.currentTimeMillis() - start;
tmp.delete(); tmp.delete();

View File

@ -24,7 +24,6 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
@ -59,7 +58,6 @@ import java.util.List;
import static com.volmit.iris.Iris.service; import static com.volmit.iris.Iris.service;
import static com.volmit.iris.core.service.EditSVC.deletingWorld; import static com.volmit.iris.core.service.EditSVC.deletingWorld;
import static com.volmit.iris.core.tools.IrisBenchmarking.inProgress;
import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode;
import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities;
import static org.bukkit.Bukkit.getServer; import static org.bukkit.Bukkit.getServer;
@ -68,14 +66,12 @@ import static org.bukkit.Bukkit.getServer;
public class CommandIris implements DecreeExecutor { public class CommandIris implements DecreeExecutor {
private CommandStudio studio; private CommandStudio studio;
private CommandPregen pregen; private CommandPregen pregen;
private CommandLazyPregen lazyPregen;
private CommandSettings settings; private CommandSettings settings;
private CommandObject object; private CommandObject object;
private CommandJigsaw jigsaw; private CommandJigsaw jigsaw;
private CommandWhat what; private CommandWhat what;
private CommandEdit edit; private CommandEdit edit;
private CommandFind find; private CommandFind find;
private CommandSupport support;
private CommandDeveloper developer; private CommandDeveloper developer;
public static boolean worldCreation = false; public static boolean worldCreation = false;
String WorldEngine; String WorldEngine;
@ -175,16 +171,6 @@ public class CommandIris implements DecreeExecutor {
sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software"); sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software");
} }
//todo Move to React
@Decree(description = "Benchmark your server", origin = DecreeOrigin.CONSOLE)
public void serverbenchmark() throws InterruptedException {
if(!inProgress) {
IrisBenchmarking.runBenchmark();
} else {
Iris.info(C.RED + "Benchmark already is in progress.");
}
}
/* /*
/todo /todo
@Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE)
@ -599,10 +585,10 @@ public class CommandIris implements DecreeExecutor {
continue; continue;
} }
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
new WorldCreator(s) WorldCreator c = new WorldCreator(s)
.generator(getDefaultWorldGenerator(s, generator)) .generator(getDefaultWorldGenerator(s, generator))
.environment(IrisData.loadAnyDimension(generator).getEnvironment()) .environment(IrisData.loadAnyDimension(generator).getEnvironment());
.createWorld(); INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -241,7 +241,8 @@ public class CommandObject implements DecreeExecutor {
Location[] b = WandSVC.getCuboid(player()); Location[] b = WandSVC.getCuboid(player());
if (b == null) { if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
return; return;
} }
Location a1 = b[0].clone(); Location a1 = b[0].clone();
@ -417,6 +418,10 @@ public class CommandObject implements DecreeExecutor {
} }
Location[] b = WandSVC.getCuboid(player()); 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 a1 = b[0].clone();
Location a2 = b[1].clone(); Location a2 = b[1].clone();
Direction d = Direction.closest(player().getLocation().getDirection()).reverse(); Direction d = Direction.closest(player().getLocation().getDirection()).reverse();
@ -477,6 +482,10 @@ public class CommandObject implements DecreeExecutor {
} }
Location[] b = WandSVC.getCuboid(player()); 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 a1 = b[0].clone();
Location a2 = b[1].clone(); Location a2 = b[1].clone();
Location a1x = b[0].clone(); Location a1x = b[0].clone();
@ -524,6 +533,10 @@ public class CommandObject implements DecreeExecutor {
} }
Location[] b = WandSVC.getCuboid(player()); 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[0].add(new Vector(0, 1, 0));
b[1].add(new Vector(0, 1, 0)); b[1].add(new Vector(0, 1, 0));
Location a1 = b[0].clone(); Location a1 = b[0].clone();

View File

@ -19,9 +19,7 @@
package com.volmit.iris.core.commands; package com.volmit.iris.core.commands;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.gui.PregeneratorJob; import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeExecutor;
@ -29,12 +27,9 @@ import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param; import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.Position2;
import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.io.File;
@Decree(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!") @Decree(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!")
public class CommandPregen implements DecreeExecutor { public class CommandPregen implements DecreeExecutor {
@Decree(description = "Pregenerate a world") @Decree(description = "Pregenerate a world")
@ -44,7 +39,9 @@ public class CommandPregen implements DecreeExecutor {
@Param(description = "The world to pregen", contextual = true) @Param(description = "The world to pregen", contextual = true)
World world, World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0") @Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
Vector center Vector center,
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
boolean gui
) { ) {
try { try {
if (sender().isPlayer() && access() == null) { if (sender().isPlayer() && access() == null) {
@ -52,13 +49,12 @@ public class CommandPregen implements DecreeExecutor {
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example."); sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
} }
radius = Math.max(radius, 1024); radius = Math.max(radius, 1024);
int w = (radius >> 9 + 1) * 2;
IrisToolbelt.pregenerate(PregenTask IrisToolbelt.pregenerate(PregenTask
.builder() .builder()
.center(new Position2(center.getBlockX() >> 9, center.getBlockZ() >> 9)) .center(new Position2(center.getBlockX(), center.getBlockZ()))
.gui(true) .gui(gui)
.width(w) .radiusX(radius)
.height(w) .radiusZ(radius)
.build(), world); .build(), world);
String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ(); String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
sender().sendMessage(msg); sender().sendMessage(msg);

View File

@ -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();
}
}
}

View File

@ -24,7 +24,6 @@ import com.volmit.iris.core.pregenerator.IrisPregenerator;
import com.volmit.iris.core.pregenerator.PregenListener; import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.PregeneratorMethod; import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
@ -41,12 +40,13 @@ import java.awt.*;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmarkInProgress;
public class PregeneratorJob implements PregenListener { public class PregeneratorJob implements PregenListener {
private static final Color COLOR_EXISTS = parseColor("#4d7d5b"); private static final Color COLOR_EXISTS = parseColor("#4d7d5b");
private static final Color COLOR_BLACK = parseColor("#4d7d5b"); private static final Color COLOR_BLACK = parseColor("#4d7d5b");
@ -56,7 +56,7 @@ public class PregeneratorJob implements PregenListener {
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c"); private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
private static final Color COLOR_GENERATED = parseColor("#65c295"); private static final Color COLOR_GENERATED = parseColor("#65c295");
private static final Color COLOR_CLEANED = parseColor("#34eb93"); 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 MemoryMonitor monitor;
private final PregenTask task; private final PregenTask task;
private final boolean saving; private final boolean saving;
@ -67,26 +67,33 @@ public class PregeneratorJob implements PregenListener {
private final Position2 max; private final Position2 max;
private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1)); private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1));
private final Engine engine; private final Engine engine;
private final ExecutorService service;
private JFrame frame; private JFrame frame;
private PregenRenderer renderer; private PregenRenderer renderer;
private int rgc = 0; private int rgc = 0;
private String[] info; private String[] info;
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) { public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
instance.updateAndGet(old -> {
if (old != null) {
old.pregenerator.close();
old.close();
}
return this;
});
this.engine = engine; this.engine = engine;
instance = this;
monitor = new MemoryMonitor(50); monitor = new MemoryMonitor(50);
saving = false; saving = false;
info = new String[]{"Initializing..."}; info = new String[]{"Initializing..."};
this.task = task; this.task = task;
this.pregenerator = new IrisPregenerator(task, method, this); this.pregenerator = new IrisPregenerator(task, method, this);
max = new Position2(0, 0); max = new Position2(0, 0);
min = new Position2(0, 0); min = new Position2(Integer.MAX_VALUE, Integer.MAX_VALUE);
task.iterateRegions((xx, zz) -> { task.iterateAllChunks((xx, zz) -> {
min.setX(Math.min(xx << 5, min.getX())); min.setX(Math.min(xx, min.getX()));
min.setZ(Math.min(zz << 5, min.getZ())); min.setZ(Math.min(zz, min.getZ()));
max.setX(Math.max((xx << 5) + 31, max.getX())); max.setX(Math.max(xx, max.getX()));
max.setZ(Math.max((zz << 5) + 31, max.getZ())); max.setZ(Math.max(zz, max.getZ()));
}); });
if (IrisSettings.get().getGui().isUseServerLaunchedGuis() && task.isGui()) { if (IrisSettings.get().getGui().isUseServerLaunchedGuis() && task.isGui()) {
@ -99,40 +106,44 @@ public class PregeneratorJob implements PregenListener {
}, "Iris Pregenerator"); }, "Iris Pregenerator");
t.setPriority(Thread.MIN_PRIORITY); t.setPriority(Thread.MIN_PRIORITY);
t.start(); t.start();
service = Executors.newVirtualThreadPerTaskExecutor();
} }
public static boolean shutdownInstance() { public static boolean shutdownInstance() {
if (instance == null) { PregeneratorJob inst = instance.get();
if (inst == null) {
return false; return false;
} }
J.a(() -> instance.pregenerator.close()); J.a(inst.pregenerator::close);
return true; return true;
} }
public static PregeneratorJob getInstance() { public static PregeneratorJob getInstance() {
return instance; return instance.get();
} }
public static boolean pauseResume() { public static boolean pauseResume() {
if (instance == null) { PregeneratorJob inst = instance.get();
if (inst == null) {
return false; return false;
} }
if (isPaused()) { if (isPaused()) {
instance.pregenerator.resume(); inst.pregenerator.resume();
} else { } else {
instance.pregenerator.pause(); inst.pregenerator.pause();
} }
return true; return true;
} }
public static boolean isPaused() { public static boolean isPaused() {
if (instance == null) { PregeneratorJob inst = instance.get();
if (inst == null) {
return true; return true;
} }
return instance.paused(); return inst.paused();
} }
private static Color parseColor(String c) { private static Color parseColor(String c) {
@ -162,7 +173,7 @@ public class PregeneratorJob implements PregenListener {
} }
public void drawRegion(int x, int z, Color color) { public void drawRegion(int x, int z, Color color) {
J.a(() -> PregenTask.iterateRegion(x, z, (xx, zz) -> { J.a(() -> task.iterateChunks(x, z, (xx, zz) -> {
draw(xx, zz, color); draw(xx, zz, color);
J.sleep(3); J.sleep(3);
})); }));
@ -182,7 +193,7 @@ public class PregeneratorJob implements PregenListener {
J.a(() -> { J.a(() -> {
pregenerator.close(); pregenerator.close();
close(); close();
instance = null; instance.compareAndSet(this, null);
}); });
} }
@ -222,10 +233,10 @@ public class PregeneratorJob implements PregenListener {
} }
@Override @Override
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) { public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
info = new String[]{ info = new String[]{
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)", (paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
"Speed: " + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m", "Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)", Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)",
"Generation Method: " + method, "Generation Method: " + method,
"Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s", "Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s",
@ -243,13 +254,16 @@ public class PregeneratorJob implements PregenListener {
} }
@Override @Override
public void onChunkGenerated(int x, int z) { public void onChunkGenerated(int x, int z, boolean cached) {
if (renderer == null || frame == null || !frame.isVisible()) return;
service.submit(() -> {
if (engine != null) { if (engine != null) {
draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8)); draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
return; return;
} }
draw(x, z, COLOR_GENERATED); draw(x, z, COLOR_GENERATED);
});
} }
@Override @Override
@ -307,8 +321,9 @@ public class PregeneratorJob implements PregenListener {
@Override @Override
public void onClose() { public void onClose() {
close(); close();
instance = null; instance.compareAndSet(this, null);
whenDone.forEach(Runnable::run); whenDone.forEach(Runnable::run);
service.shutdownNow();
} }
@Override @Override

View File

@ -18,20 +18,33 @@
package com.volmit.iris.core.link; 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.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.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection; import java.util.*;
import java.util.List;
public class MythicMobsLink { public class MythicMobsLink {
public MythicMobsLink() { public MythicMobsLink() {
if (getPlugin() == null) return;
Iris.instance.registerListener(new ConditionListener());
} }
public boolean isEnabled() { public boolean isEnabled() {
@ -49,12 +62,70 @@ public class MythicMobsLink {
* @param location The location * @param location The location
* @return The mob, or null if it can't be spawned * @return The mob, or null if it can't be spawned
*/ */
public @Nullable public @Nullable Entity spawnMob(String mob, Location location) {
Entity spawnMob(String mob, Location location) {
return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null; return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null;
} }
public Collection<String> getMythicMobTypes() { public Collection<String> getMythicMobTypes() {
return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of(); 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());
}
}
} }

View File

@ -47,6 +47,7 @@ public class WorldEditLink {
} catch (Throwable e) { } catch (Throwable e) {
Iris.error("Could not get selection"); Iris.error("Could not get selection");
e.printStackTrace(); e.printStackTrace();
Iris.reportError(e);
active.reset(); active.reset();
active.aquire(() -> false); active.aquire(() -> false);
} }

View File

@ -300,6 +300,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return r; return r;
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
Iris.error("Failed to create loader! " + registrant.getCanonicalName()); Iris.error("Failed to create loader! " + registrant.getCanonicalName());
} }

View File

@ -45,6 +45,7 @@ import lombok.ToString;
import java.io.*; import java.io.*;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -240,8 +241,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
for (String i : s) { for (String i : s) {
burst.queue(() -> { burst.queue(() -> {
T t = load(i); T t = load(i);
if (t == null)
return;
if (t != null) { synchronized (m) {
m.add(t); m.add(t);
} }
}); });

View File

@ -34,10 +34,11 @@ public class INMS {
"1.21.1", "v1_21_R1", "1.21.1", "v1_21_R1",
"1.21.2", "v1_21_R2", "1.21.2", "v1_21_R2",
"1.21.3", "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( private static final List<Version> PACKS = List.of(
new Version(21, 4, "31010"), new Version(21, 4, "31020"),
new Version(21, 2, "31000"), new Version(21, 2, "31000"),
new Version(20, 1, "3910") new Version(20, 1, "3910")
); );

View File

@ -18,6 +18,7 @@
package com.volmit.iris.core.nms; package com.volmit.iris.core.nms;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
@ -30,15 +31,12 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color; import java.awt.Color;
public interface INMSBinding { public interface INMSBinding {
@ -91,8 +89,14 @@ public interface INMSBinding {
MCABiomeContainer newBiomeContainer(int min, int max); MCABiomeContainer newBiomeContainer(int min, int max);
default World createWorld(WorldCreator c) { default World createWorld(WorldCreator c) {
if (missingDimensionTypes(true, true, true))
throw new IllegalStateException("Missing dimenstion types to create world");
try (var ignored = injectLevelStems()) {
ignored.storeContext();
return c.createWorld(); return c.createWorld();
} }
}
int countCustomBiomes(); int countCustomBiomes();
@ -125,4 +129,14 @@ public interface INMSBinding {
} }
KList<String> getStructureKeys(); KList<String> getStructureKeys();
AutoClosing injectLevelStems();
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
return null;
}
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
void removeCustomDimensions(World world);
} }

View File

@ -0,0 +1,39 @@
package com.volmit.iris.core.nms.container;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.function.NastyRunnable;
import lombok.AllArgsConstructor;
import java.util.concurrent.atomic.AtomicBoolean;
@AllArgsConstructor
public class AutoClosing implements AutoCloseable {
private static final KMap<Thread, AutoClosing> CONTEXTS = new KMap<>();
private final AtomicBoolean closed = new AtomicBoolean();
private final NastyRunnable action;
@Override
public void close() {
if (closed.getAndSet(true)) return;
try {
removeContext();
action.run();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public void storeContext() {
CONTEXTS.put(Thread.currentThread(), this);
}
public void removeContext() {
CONTEXTS.values().removeIf(c -> c == this);
}
public static void closeContext() {
AutoClosing closing = CONTEXTS.remove(Thread.currentThread());
if (closing == null) return;
closing.close();
}
}

View File

@ -20,46 +20,6 @@ public interface IDataFixer {
return obj; return obj;
} }
default JSONObject createPreset() {
return new JSONObject("""
{
"dimensions": {
"minecraft:overworld": {
"type": "iris:overworld",
"generator": {
"type": "minecraft:noise",
"biome_source": {
"type": "minecraft:multi_noise",
"preset": "minecraft:overworld"
},
"settings": "minecraft:overworld"
}
},
"minecraft:the_end": {
"type": "iris:the_end",
"generator": {
"type": "minecraft:noise",
"biome_source": {
"type": "minecraft:the_end"
},
"settings": "minecraft:end"
}
},
"minecraft:the_nether": {
"type": "iris:the_nether",
"generator": {
"type": "minecraft:noise",
"biome_source": {
"type": "minecraft:multi_noise",
"preset": "minecraft:nether"
},
"settings": "minecraft:nether"
}
}
}
}""");
}
enum Dimension { enum Dimension {
OVERRWORLD, OVERRWORLD,
NETHER, NETHER,

View File

@ -20,7 +20,9 @@ package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; 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.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
@ -118,6 +120,26 @@ public class NMSBinding1X implements INMSBinding {
return new KList<>(list); return new KList<>(list);
} }
@Override
public AutoClosing injectLevelStems() {
return new AutoClosing(() -> {});
}
@Override
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
return injectLevelStems();
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
return false;
}
@Override
public void removeCustomDimensions(World world) {
}
@Override @Override
public CompoundTag serializeEntity(Entity location) { public CompoundTag serializeEntity(Entity location) {
return null; return null;

View File

@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
public class ChunkUpdater { public class ChunkUpdater {
private static final String REGION_PATH = "region" + File.separator + "r.";
private final AtomicBoolean paused = new AtomicBoolean(); private final AtomicBoolean paused = new AtomicBoolean();
private final AtomicBoolean cancelled = new AtomicBoolean(); private final AtomicBoolean cancelled = new AtomicBoolean();
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>(); private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
@ -108,6 +109,7 @@ public class ChunkUpdater {
} }
} }
} catch (Exception e) { } catch (Exception e) {
Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }
}, 0, 3, TimeUnit.SECONDS); }, 0, 3, TimeUnit.SECONDS);
@ -162,11 +164,14 @@ public class ChunkUpdater {
J.sleep(50); J.sleep(50);
} }
if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) { if (rX < dimensions.min.getX() ||
return; rX > dimensions.max.getX() ||
} rZ < dimensions.min.getZ() ||
rZ > dimensions.max.getZ() ||
!new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists()
) return;
PregenTask.iterateRegion(rX, rZ, (x, z) -> { task.iterateChunks(rX, rZ, (x, z) -> {
while (paused.get() && !cancelled.get()) { while (paused.get() && !cancelled.get()) {
J.sleep(50); J.sleep(50);
} }
@ -310,6 +315,7 @@ public class ChunkUpdater {
world.save(); world.save();
}).get(); }).get();
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -348,8 +354,8 @@ public class ChunkUpdater {
int width = maxZ - minZ + 1; int width = maxZ - minZ + 1;
return new Dimensions(new Position2(minX, minZ), new Position2(maxX, maxZ), height * width, PregenTask.builder() return new Dimensions(new Position2(minX, minZ), new Position2(maxX, maxZ), height * width, PregenTask.builder()
.width((int) Math.ceil(width / 2d)) .radiusZ((int) Math.ceil(width / 2d * 512))
.height((int) Math.ceil(height / 2d)) .radiusX((int) Math.ceil(height / 2d * 512))
.center(new Position2(oX, oZ)) .center(new Position2(oX, oZ))
.build()); .build());
} }

View File

@ -19,7 +19,6 @@
package com.volmit.iris.core.pregenerator; package com.volmit.iris.core.pregenerator;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.pack.IrisPack;
import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.collection.KSet;
@ -32,28 +31,33 @@ import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
public class IrisPregenerator { public class IrisPregenerator {
private static final double INVALID = 9223372036854775807d;
private final PregenTask task; private final PregenTask task;
private final PregeneratorMethod generator; private final PregeneratorMethod generator;
private final PregenListener listener; private final PregenListener listener;
private final Looper ticker; private final Looper ticker;
private final AtomicBoolean paused; private final AtomicBoolean paused;
private final AtomicBoolean shutdown; private final AtomicBoolean shutdown;
private final RollingSequence cachedPerSecond;
private final RollingSequence chunksPerSecond; private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute; private final RollingSequence chunksPerMinute;
private final RollingSequence regionsPerMinute; private final RollingSequence regionsPerMinute;
private final KList<Integer> chunksPerSecondHistory; private final KList<Integer> chunksPerSecondHistory;
private static AtomicInteger generated; private final AtomicLong generated;
private final AtomicInteger generatedLast; private final AtomicLong generatedLast;
private final AtomicInteger generatedLastMinute; private final AtomicLong generatedLastMinute;
private static AtomicInteger totalChunks; private final AtomicLong cached;
private final AtomicLong cachedLast;
private final AtomicLong cachedLastMinute;
private final AtomicLong totalChunks;
private final AtomicLong startTime; private final AtomicLong startTime;
private final ChronoLatch minuteLatch; private final ChronoLatch minuteLatch;
private final AtomicReference<String> currentGeneratorMethod; private final AtomicReference<String> currentGeneratorMethod;
@ -62,8 +66,10 @@ public class IrisPregenerator {
private final KSet<Position2> net; private final KSet<Position2> net;
private final ChronoLatch cl; private final ChronoLatch cl;
private final ChronoLatch saveLatch = new ChronoLatch(30000); private final ChronoLatch saveLatch = new ChronoLatch(30000);
private final IrisPackBenchmarking benchmarking;
public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) { public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) {
benchmarking = IrisPackBenchmarking.getInstance();
this.listener = listenify(listener); this.listener = listenify(listener);
cl = new ChronoLatch(5000); cl = new ChronoLatch(5000);
generatedRegions = new KSet<>(); generatedRegions = new KSet<>();
@ -75,46 +81,71 @@ public class IrisPregenerator {
net = new KSet<>(); net = new KSet<>();
currentGeneratorMethod = new AtomicReference<>("Void"); currentGeneratorMethod = new AtomicReference<>("Void");
minuteLatch = new ChronoLatch(60000, false); minuteLatch = new ChronoLatch(60000, false);
cachedPerSecond = new RollingSequence(5);
chunksPerSecond = new RollingSequence(10); chunksPerSecond = new RollingSequence(10);
chunksPerMinute = new RollingSequence(10); chunksPerMinute = new RollingSequence(10);
regionsPerMinute = new RollingSequence(10); regionsPerMinute = new RollingSequence(10);
chunksPerSecondHistory = new KList<>(); chunksPerSecondHistory = new KList<>();
generated = new AtomicInteger(0); generated = new AtomicLong(0);
generatedLast = new AtomicInteger(0); generatedLast = new AtomicLong(0);
generatedLastMinute = new AtomicInteger(0); generatedLastMinute = new AtomicLong(0);
totalChunks = new AtomicInteger(0); cached = new AtomicLong();
task.iterateRegions((_a, _b) -> totalChunks.addAndGet(1024)); cachedLast = new AtomicLong(0);
cachedLastMinute = new AtomicLong(0);
totalChunks = new AtomicLong(0);
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
startTime = new AtomicLong(M.ms()); startTime = new AtomicLong(M.ms());
ticker = new Looper() { ticker = new Looper() {
@Override @Override
protected long loop() { protected long loop() {
long eta = computeETA(); long eta = computeETA();
int secondGenerated = generated.get() - generatedLast.get();
long secondCached = cached.get() - cachedLast.get();
cachedLast.set(cached.get());
cachedPerSecond.put(secondCached);
long secondGenerated = generated.get() - generatedLast.get() - secondCached;
generatedLast.set(generated.get()); generatedLast.set(generated.get());
if (secondCached == 0 || secondGenerated != 0) {
chunksPerSecond.put(secondGenerated); chunksPerSecond.put(secondGenerated);
chunksPerSecondHistory.add(secondGenerated); chunksPerSecondHistory.add((int) secondGenerated);
}
if (minuteLatch.flip()) { if (minuteLatch.flip()) {
int minuteGenerated = generated.get() - generatedLastMinute.get(); long minuteCached = cached.get() - cachedLastMinute.get();
cachedLastMinute.set(cached.get());
long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached;
generatedLastMinute.set(generated.get()); generatedLastMinute.set(generated.get());
if (minuteCached == 0 || minuteGenerated != 0) {
chunksPerMinute.put(minuteGenerated); chunksPerMinute.put(minuteGenerated);
regionsPerMinute.put((double) minuteGenerated / 1024D); regionsPerMinute.put((double) minuteGenerated / 1024D);
} }
}
boolean cached = cachedPerSecond.getAverage() != 0;
listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(), listener.onTick(
cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(),
chunksPerMinute.getAverage(),
regionsPerMinute.getAverage(), regionsPerMinute.getAverage(),
(double) generated.get() / (double) totalChunks.get(), (double) generated.get() / (double) totalChunks.get(), generated.get(),
generated.get(), totalChunks.get(), totalChunks.get(),
totalChunks.get() - generated.get(), totalChunks.get() - generated.get(), eta, M.ms() - startTime.get(), currentGeneratorMethod.get(),
eta, M.ms() - startTime.get(), currentGeneratorMethod.get()); cached);
if (cl.flip()) { if (cl.flip()) {
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100; double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
if (!IrisPackBenchmarking.benchmarkInProgress) {
Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
} else { benchmarking != null ? "Benchmarking" : "Pregen",
Iris.info("Benchmarking: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); Form.f(generated.get()),
} Form.f(totalChunks.get()),
percentage,
cached ?
"Cached " + Form.f((int) cachedPerSecond.getAverage()) :
Form.f((int) chunksPerSecond.getAverage()),
Form.duration(eta, 2)
);
} }
return 1000; return 1000;
} }
@ -122,12 +153,13 @@ public class IrisPregenerator {
} }
private long computeETA() { private long computeETA() {
return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total? double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
// If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers) // If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers)
((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) : ((totalChunks.get() - generated.get() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) :
// If no, use quick function (which is less accurate over time but responds better to the initial delay) // If no, use quick function (which is less accurate over time but responds better to the initial delay)
((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000 ((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000
); );
return Double.isFinite(d) && d != INVALID ? (long) d : 0;
} }
@ -139,13 +171,15 @@ public class IrisPregenerator {
init(); init();
ticker.start(); ticker.start();
checkRegions(); checkRegions();
var p = PrecisionStopwatch.start();
task.iterateRegions((x, z) -> visitRegion(x, z, true)); task.iterateRegions((x, z) -> visitRegion(x, z, true));
task.iterateRegions((x, z) -> visitRegion(x, z, false)); task.iterateRegions((x, z) -> visitRegion(x, z, false));
Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
shutdown(); shutdown();
if (!IrisPackBenchmarking.benchmarkInProgress) { if (benchmarking == null) {
Iris.info(C.IRIS + "Pregen stopped."); Iris.info(C.IRIS + "Pregen stopped.");
} else { } else {
IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory); benchmarking.finishedBenchmark(chunksPerSecondHistory);
} }
} }
@ -194,7 +228,7 @@ public class IrisPregenerator {
} else if (!regions) { } else if (!regions) {
hit = true; hit = true;
listener.onRegionGenerating(x, z); listener.onRegionGenerating(x, z);
PregenTask.iterateRegion(x, z, (xx, zz) -> { task.iterateChunks(x, z, (xx, zz) -> {
while (paused.get() && !shutdown.get()) { while (paused.get() && !shutdown.get()) {
J.sleep(50); J.sleep(50);
} }
@ -235,8 +269,8 @@ public class IrisPregenerator {
private PregenListener listenify(PregenListener listener) { private PregenListener listenify(PregenListener listener) {
return new PregenListener() { return new PregenListener() {
@Override @Override
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) { public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method); listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method, cached);
} }
@Override @Override
@ -245,9 +279,10 @@ public class IrisPregenerator {
} }
@Override @Override
public void onChunkGenerated(int x, int z) { public void onChunkGenerated(int x, int z, boolean c) {
listener.onChunkGenerated(x, z); listener.onChunkGenerated(x, z, c);
generated.addAndGet(1); generated.addAndGet(1);
if (c) cached.addAndGet(1);
} }
@Override @Override

View File

@ -19,11 +19,15 @@
package com.volmit.iris.core.pregenerator; package com.volmit.iris.core.pregenerator;
public interface PregenListener { public interface PregenListener {
void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method); void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached);
void onChunkGenerating(int x, int z); void onChunkGenerating(int x, int z);
void onChunkGenerated(int x, int z); default void onChunkGenerated(int x, int z) {
onChunkGenerated(x, z, false);
}
void onChunkGenerated(int x, int z, boolean cached);
void onRegionGenerated(int x, int z); void onRegionGenerated(int x, int z);

View File

@ -32,17 +32,26 @@ import java.util.Comparator;
@Data @Data
public class PregenTask { public class PregenTask {
private static final Position2 ZERO = new Position2(0, 0); private static final Position2 ZERO = new Position2(0, 0);
private static final KList<Position2> ORDER_CENTER = computeChunkOrder();
private static final KMap<Position2, KList<Position2>> ORDERS = new KMap<>(); private static final KMap<Position2, KList<Position2>> ORDERS = new KMap<>();
@Builder.Default @Builder.Default
private boolean gui = false; private final boolean gui = false;
@Builder.Default @Builder.Default
private Position2 center = new Position2(0, 0); private final Position2 center = new Position2(0, 0);
@Builder.Default @Builder.Default
private int width = 1; private final int radiusX = 1;
@Builder.Default @Builder.Default
private int height = 1; private final int radiusZ = 1;
private final Bounds bounds = new Bounds();
protected PregenTask(boolean gui, Position2 center, int radiusX, int radiusZ) {
this.gui = gui;
this.center = new ProxiedPos(center);
this.radiusX = radiusX;
this.radiusZ = radiusZ;
bounds.update();
}
public static void iterateRegion(int xr, int zr, Spiraled s, Position2 pull) { public static void iterateRegion(int xr, int zr, Spiraled s, Position2 pull) {
for (Position2 i : ORDERS.computeIfAbsent(pull, PregenTask::computeOrder)) { for (Position2 i : ORDERS.computeIfAbsent(pull, PregenTask::computeOrder)) {
@ -70,29 +79,72 @@ public class PregenTask {
return p; return p;
} }
private static KList<Position2> computeChunkOrder() {
Position2 center = new Position2(15, 15);
KList<Position2> p = new KList<>();
new Spiraler(33, 33, (x, z) -> {
int xx = x + 15;
int zz = z + 15;
if (xx < 0 || xx > 31 || zz < 0 || zz > 31) {
return;
}
p.add(new Position2(xx, zz));
}).drain();
p.sort(Comparator.comparing((i) -> i.distance(center)));
return p;
}
public void iterateRegions(Spiraled s) { public void iterateRegions(Spiraled s) {
new Spiraler(getWidth() * 2, getHeight() * 2, s) var bound = bounds.region();
.setOffset(center.getX(), center.getZ()).drain(); new Spiraler(bound.sizeX, bound.sizeZ, ((x, z) -> {
if (bound.check(x, z)) s.on(x, z);
})).setOffset(center.getX() >> 9, center.getZ() >> 9).drain();
}
public void iterateChunks(int rX, int rZ, Spiraled s) {
var bound = bounds.chunk();
iterateRegion(rX, rZ, ((x, z) -> {
if (bound.check(x, z)) s.on(x, z);
}));
} }
public void iterateAllChunks(Spiraled s) { public void iterateAllChunks(Spiraled s) {
new Spiraler(getWidth() * 2, getHeight() * 2, (x, z) -> iterateRegion(x, z, s)) iterateRegions(((rX, rZ) -> iterateChunks(rX, rZ, s)));
.setOffset(center.getX(), center.getZ()).drain(); }
private class Bounds {
private Bound chunk = null;
private Bound region = null;
public void update() {
int maxX = center.getX() + radiusX;
int maxZ = center.getZ() + radiusZ;
int minX = center.getX() - radiusX;
int minZ = center.getZ() - radiusZ;
chunk = new Bound(minX >> 4, minZ >> 4, Math.ceilDiv(maxX, 16), Math.ceilDiv(maxZ, 16));
region = new Bound(minX >> 9, minZ >> 9, Math.ceilDiv(maxX, 512), Math.ceilDiv(maxZ, 512));
}
public Bound chunk() {
if (chunk == null) update();
return chunk;
}
public Bound region() {
if (region == null) update();
return region;
}
}
private record Bound(int minX, int maxX, int minZ, int maxZ, int sizeX, int sizeZ) {
private Bound(int minX, int minZ, int maxX, int maxZ) {
this(minX, maxX, minZ, maxZ, maxZ - minZ + 1, maxZ - minZ + 1);
}
boolean check(int x, int z) {
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
}
}
private static class ProxiedPos extends Position2 {
public ProxiedPos(Position2 p) {
super(p.getX(), p.getZ());
}
@Override
public void setX(int x) {
throw new IllegalStateException("This Position2 may not be modified");
}
@Override
public void setZ(int z) {
throw new IllegalStateException("This Position2 may not be modified");
}
} }
} }

View File

@ -0,0 +1,70 @@
package com.volmit.iris.core.pregenerator.cache;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
import java.io.File;
public interface PregenCache {
default boolean isThreadSafe() {
return false;
}
@ChunkCoordinates
boolean isChunkCached(int x, int z);
@RegionCoordinates
boolean isRegionCached(int x, int z);
@ChunkCoordinates
void cacheChunk(int x, int z);
@RegionCoordinates
void cacheRegion(int x, int z);
void write();
static PregenCache create(File directory) {
if (directory == null) return EMPTY;
return new PregenCacheImpl(directory);
}
default PregenCache sync() {
if (isThreadSafe()) return this;
return new SynchronizedCache(this);
}
PregenCache EMPTY = new PregenCache() {
@Override
public boolean isThreadSafe() {
return true;
}
@Override
public boolean isChunkCached(int x, int z) {
return false;
}
@Override
public boolean isRegionCached(int x, int z) {
return false;
}
@Override
public void cacheChunk(int x, int z) {
}
@Override
public void cacheRegion(int x, int z) {
}
@Override
public void write() {
}
};
}

View File

@ -0,0 +1,218 @@
package com.volmit.iris.core.pregenerator.cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
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;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.jetbrains.annotations.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@NotThreadSafe
@RequiredArgsConstructor
class PregenCacheImpl implements PregenCache {
private static final int SIZE = 32;
private final File directory;
private final HyperLock hyperLock = new HyperLock(SIZE * 2, true);
private final LoadingCache<Pos, Plate> cache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.maximumSize(SIZE)
.removalListener(this::onRemoval)
.evictionListener(this::onRemoval)
.build(this::load);
@ChunkCoordinates
public boolean isChunkCached(int x, int z) {
var plate = cache.get(new Pos(x >> 10, z >> 10));
if (plate == null) return false;
return plate.isCached((x >> 5) & 31, (z >> 5) & 31, r -> r.isCached(x & 31, z & 31));
}
@RegionCoordinates
public boolean isRegionCached(int x, int z) {
var plate = cache.get(new Pos(x >> 5, z >> 5));
if (plate == null) return false;
return plate.isCached(x & 31, z & 31, Region::isCached);
}
@ChunkCoordinates
public void cacheChunk(int x, int z) {
var plate = cache.get(new Pos(x >> 10, z >> 10));
plate.cache((x >> 5) & 31, (z >> 5) & 31, r -> r.cache(x & 31, z & 31));
}
@RegionCoordinates
public void cacheRegion(int x, int z) {
var plate = cache.get(new Pos(x >> 5, z >> 5));
plate.cache(x & 31, z & 31, Region::cache);
}
public void write() {
cache.asMap().values().forEach(this::write);
}
private Plate load(Pos key) {
hyperLock.lock(key.x, key.z);
try {
File file = fileForPlate(key);
if (!file.exists()) return new Plate(key);
try (var in = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) {
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);
}
} finally {
hyperLock.unlock(key.x, key.z);
}
}
private void write(Plate plate) {
hyperLock.lock(plate.pos.x, plate.pos.z);
try {
File file = fileForPlate(plate.pos);
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 {
hyperLock.unlock(plate.pos.x, plate.pos.z);
}
}
private void onRemoval(@Nullable Pos key, @Nullable Plate plate, RemovalCause cause) {
if (plate == null) return;
write(plate);
}
private File fileForPlate(Pos pos) {
if (!directory.exists() && !directory.mkdirs())
throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath());
return new File(directory, "c." + pos.x + "." + pos.z + ".lz4b");
}
private static class Plate {
private final Pos pos;
private short count;
private Region[] regions;
public Plate(Pos pos) {
this.pos = pos;
count = 0;
regions = new Region[1024];
}
public Plate(Pos pos, DataInput in) throws IOException {
this.pos = pos;
count = (short) Varint.readSignedVarInt(in);
if (count == 1024) return;
regions = new Region[1024];
for (int i = 0; i < 1024; i++) {
if (in.readBoolean()) continue;
regions[i] = new Region(in);
}
}
public boolean isCached(int x, int z, Predicate<Region> predicate) {
if (count == 1024) return true;
Region region = regions[x * 32 + z];
if (region == null) return false;
return predicate.test(region);
}
public void cache(int x, int z, Predicate<Region> predicate) {
if (count == 1024) return;
Region region = regions[x * 32 + z];
if (region == null) regions[x * 32 + z] = region = new Region();
if (predicate.test(region)) count++;
}
public void write(DataOutput out) throws IOException {
Varint.writeSignedVarInt(count, out);
if (count == 1024) return;
for (Region region : regions) {
out.writeBoolean(region == null);
if (region == null) continue;
region.write(out);
}
}
}
private static class Region {
private short count;
private long[] words;
public Region() {
count = 0;
words = new long[64];
}
public Region(DataInput in) throws IOException {
count = (short) Varint.readSignedVarInt(in);
if (count == 1024) return;
words = new long[64];
for (int i = 0; i < 64; i++) {
words[i] = Varint.readUnsignedVarLong(in);
}
}
public boolean cache() {
if (count == 1024) return false;
count = 1024;
words = null;
return true;
}
public boolean cache(int x, int z) {
if (count == 1024) return false;
int i = x * 32 + z;
int w = i >> 6;
long b = 1L << (i & 63);
var cur = (words[w] & b) != 0;
if (cur) return false;
if (++count == 1024) {
words = null;
return true;
} else words[w] |= b;
return false;
}
public boolean isCached() {
return count == 1024;
}
public boolean isCached(int x, int z) {
int i = x * 32 + z;
return count == 1024 || (words[i >> 6] & 1L << (i & 63)) != 0;
}
public void write(DataOutput out) throws IOException {
Varint.writeSignedVarInt(count, out);
if (isCached()) return;
for (long word : words) {
Varint.writeUnsignedVarLong(word, out);
}
}
}
private record Pos(int x, int z) {}
}

View File

@ -0,0 +1,48 @@
package com.volmit.iris.core.pregenerator.cache;
import lombok.AllArgsConstructor;
@AllArgsConstructor
class SynchronizedCache implements PregenCache {
private final PregenCache cache;
@Override
public boolean isThreadSafe() {
return true;
}
@Override
public boolean isChunkCached(int x, int z) {
synchronized (cache) {
return cache.isChunkCached(x, z);
}
}
@Override
public boolean isRegionCached(int x, int z) {
synchronized (cache) {
return cache.isRegionCached(x, z);
}
}
@Override
public void cacheChunk(int x, int z) {
synchronized (cache) {
cache.cacheChunk(x, z);
}
}
@Override
public void cacheRegion(int x, int z) {
synchronized (cache) {
cache.cacheRegion(x, z);
}
}
@Override
public void write() {
synchronized (cache) {
cache.write();
}
}
}

View File

@ -19,6 +19,7 @@
package com.volmit.iris.core.pregenerator.methods; package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.PregenListener; import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod; import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
@ -31,24 +32,32 @@ import io.papermc.lib.PaperLib;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.World; import org.bukkit.World;
import java.util.ArrayList; import java.lang.reflect.InvocationTargetException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class AsyncPregenMethod implements PregeneratorMethod { public class AsyncPregenMethod implements PregeneratorMethod {
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private final World world; private final World world;
private final MultiBurst burst; private final Executor executor;
private final Semaphore semaphore; private final Semaphore semaphore;
private final int threads;
private final boolean urgent;
private final Map<Chunk, Long> lastUse; private final Map<Chunk, Long> lastUse;
public AsyncPregenMethod(World world, int threads) { public AsyncPregenMethod(World world, int unusedThreads) {
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!"); throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
} }
this.world = world; this.world = world;
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
semaphore = new Semaphore(256); this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
this.semaphore = new Semaphore(this.threads, true);
this.urgent = IrisSettings.get().getPregen().useHighPriority;
this.lastUse = new KMap<>(); this.lastUse = new KMap<>();
} }
@ -60,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return; return;
} }
for (Chunk i : new ArrayList<>(lastUse.keySet())) { long minTime = M.ms() - 10_000;
Long lastUseTime = lastUse.get(i); lastUse.entrySet().removeIf(i -> {
if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) { final Chunk chunk = i.getKey();
i.unload(); final Long lastUseTime = i.getValue();
lastUse.remove(i); if (!chunk.isLoaded() || lastUseTime == null)
} return true;
if (lastUseTime < minTime) {
chunk.unload();
return true;
} }
return false;
});
world.save(); world.save();
}).get(); }).get();
} catch (Throwable e) { } catch (Throwable e) {
@ -74,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 @Override
public void init() { public void init() {
unloadAndSaveAllChunks(); unloadAndSaveAllChunks();
increaseWorkerThreads();
} }
@Override @Override
@ -101,9 +101,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
@Override @Override
public void close() { public void close() {
semaphore.acquireUninterruptibly(256); semaphore.acquireUninterruptibly(threads);
unloadAndSaveAllChunks(); unloadAndSaveAllChunks();
burst.close(); executor.shutdown();
resetWorkerThreads();
} }
@Override @Override
@ -129,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
} catch (InterruptedException e) { } catch (InterruptedException e) {
return; return;
} }
burst.complete(() -> completeChunk(x, z, listener)); executor.generate(x, z, listener);
} }
@Override @Override
@ -140,4 +141,100 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return null; 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());
});
}
}
} }

View File

@ -0,0 +1,86 @@
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.Iris;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.pregenerator.cache.PregenCache;
import com.volmit.iris.core.service.GlobalCacheSVC;
import com.volmit.iris.util.mantle.Mantle;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class CachedPregenMethod implements PregeneratorMethod {
private final PregeneratorMethod method;
private final PregenCache cache;
public CachedPregenMethod(PregeneratorMethod method, String worldName) {
this.method = method;
var cache = Iris.service(GlobalCacheSVC.class).get(worldName);
if (cache == null) {
Iris.debug("Could not find existing cache for " + worldName + " creating fallback");
cache = GlobalCacheSVC.createDefault(worldName);
}
this.cache = cache;
}
@Override
public void init() {
method.init();
}
@Override
public void close() {
method.close();
cache.write();
}
@Override
public void save() {
method.save();
cache.write();
}
@Override
public boolean supportsRegions(int x, int z, PregenListener listener) {
return cache.isRegionCached(x, z) || method.supportsRegions(x, z, listener);
}
@Override
public String getMethod(int x, int z) {
return method.getMethod(x, z);
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
if (cache.isRegionCached(x, z)) {
listener.onRegionGenerated(x, z);
int rX = x << 5, rZ = z << 5;
for (int cX = 0; cX < 32; cX++) {
for (int cZ = 0; cZ < 32; cZ++) {
listener.onChunkGenerated(rX + cX, rZ + cZ, true);
listener.onChunkCleaned(rX + cX, rZ + cZ);
}
}
return;
}
method.generateRegion(x, z, listener);
cache.cacheRegion(x, z);
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
if (cache.isChunkCached(x, z)) {
listener.onChunkGenerated(x, z, true);
listener.onChunkCleaned(x, z);
return;
}
method.generateChunk(x, z, listener);
cache.cacheChunk(x, z);
}
@Override
public Mantle getMantle() {
return method.getMantle();
}
}

View File

@ -1,6 +1,7 @@
package com.volmit.iris.core.safeguard; package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
public class IrisSafeguard { public class IrisSafeguard {
public static boolean unstablemode = false; public static boolean unstablemode = false;
@ -11,5 +12,23 @@ public class IrisSafeguard {
Iris.info("Enabled Iris SafeGuard"); Iris.info("Enabled Iris SafeGuard");
ServerBootSFG.BootCheck(); ServerBootSFG.BootCheck();
} }
public static void earlySplash() {
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)
return;
Iris.instance.splash();
UtilsSFG.splash();
}
public static String mode() {
if (unstablemode) {
return "unstable";
} else if (warningmode) {
return "warning";
} else {
return "stable";
}
}
} }

View File

@ -3,6 +3,7 @@ package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.IrisContextInjector;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
@ -29,6 +30,7 @@ public class ServerBootSFG {
public static boolean isJRE = false; public static boolean isJRE = false;
public static boolean hasPrivileges = true; public static boolean hasPrivileges = true;
public static boolean unsuportedversion = false; public static boolean unsuportedversion = false;
public static boolean missingDimensionTypes = false;
protected static boolean safeguardPassed; protected static boolean safeguardPassed;
public static boolean passedserversoftware = true; public static boolean passedserversoftware = true;
protected static int count; protected static int count;
@ -110,6 +112,12 @@ public class ServerBootSFG {
severityMedium++; severityMedium++;
} }
if (IrisContextInjector.isMissingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
allIncompatibilities = joiner.toString(); allIncompatibilities = joiner.toString();
safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0); safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0);
@ -152,13 +160,9 @@ public class ServerBootSFG {
} }
public static boolean enoughDiskSpace() { public static boolean enoughDiskSpace() {
File freeSpace = new File(Bukkit.getWorldContainer() + "."); File freeSpace = Bukkit.getWorldContainer();
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0); double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
if (gigabytes > 3){ return gigabytes > 3;
return true;
} else {
return false;
}
} }
private static boolean checkJavac(String path) { private static boolean checkJavac(String path) {

View File

@ -39,6 +39,11 @@ public class UtilsSFG {
Iris.safeguard(C.RED + "Server Version"); Iris.safeguard(C.RED + "Server Version");
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4"); Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
} }
if (ServerBootSFG.missingDimensionTypes) {
Iris.safeguard(C.RED + "Dimension Types");
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
}
if (!ServerBootSFG.passedserversoftware) { if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software"); Iris.safeguard(C.YELLOW + "Unsupported Server Software");
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead."); Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");

View File

@ -0,0 +1,108 @@
package com.volmit.iris.core.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.cache.PregenCache;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.plugin.IrisService;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.function.Function;
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);
}
@Override
public void onDisable() {
disabled = true;
globalCache.qclear((world, cache) -> cache.write());
}
@Nullable
public PregenCache get(@NonNull World world) {
return globalCache.get(world.getName());
}
@Nullable
public PregenCache get(@NonNull String world) {
return globalCache.get(world);
}
@EventHandler(priority = EventPriority.MONITOR)
public void on(WorldInitEvent event) {
if (isDisabled()) return;
createCache(event.getWorld());
}
@EventHandler(priority = EventPriority.MONITOR)
public void on(WorldUnloadEvent event) {
var cache = globalCache.remove(event.getWorld().getName());
if (cache == null) return;
cache.write();
}
@EventHandler(priority = EventPriority.MONITOR)
public void on(ChunkLoadEvent event) {
var cache = get(event.getWorld());
if (cache == null) return;
cache.cacheChunk(event.getChunk().getX(), event.getChunk().getZ());
}
private void createCache(World world) {
globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
}
private boolean isDisabled() {
boolean conf = IrisSettings.get().getWorld().isGlobalPregenCache();
if (lastState != conf)
return lastState;
if (conf) {
Bukkit.getWorlds().forEach(this::createCache);
} else {
globalCache.values().removeIf(cache -> {
cache.write();
return true;
});
}
return lastState = !conf;
}
@NonNull
public static PregenCache createCache(@NonNull String worldName, @NonNull Function<String, PregenCache> provider) {
return REFERENCE_CACHE.get(worldName, provider);
}
@NonNull
public static PregenCache createDefault(@NonNull String worldName) {
return createCache(worldName, GlobalCacheSVC::createDefault0);
}
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();
}
}

View File

@ -1,317 +1,246 @@
package com.volmit.iris.core.service; package com.volmit.iris.core.service;
import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator; 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.collection.KMap;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.TectonicPlate; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.IrisService; 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.Looper;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Synchronized;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.event.EventHandler; 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.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent; 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.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
public class IrisEngineSVC implements IrisService { public class IrisEngineSVC implements IrisService {
public static IrisEngineSVC instance; private final AtomicInteger tectonicLimit = new AtomicInteger(30);
public boolean isServerShuttingDown = false; private final AtomicInteger tectonicPlates = new AtomicInteger();
public boolean isServerLoaded = false; private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
private static final AtomicInteger tectonicLimit = new AtomicInteger(30); private final AtomicInteger trimmerAlive = new AtomicInteger();
private ReentrantLock lastUseLock; private final AtomicInteger unloaderAlive = new AtomicInteger();
private KMap<World, Long> lastUse; private final AtomicInteger totalWorlds = new AtomicInteger();
private List<World> IrisWorlds; private final AtomicDouble maxIdleDuration = new AtomicDouble();
private Looper cacheTicker; private final AtomicDouble minIdleDuration = new AtomicDouble();
private Looper trimTicker; private final AtomicLong loadedChunks = new AtomicLong();
private Looper unloadTicker; private final KMap<World, Registered> worlds = new KMap<>();
private ScheduledExecutorService service;
private Looper updateTicker; 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 @Override
public void onEnable() { public void onEnable() {
this.cl = new ChronoLatch(5000); var settings = IrisSettings.get().getPerformance();
lastUse = new KMap<>(); var engine = settings.getEngineSVC();
lastUseLock = new ReentrantLock(); service = Executors.newScheduledThreadPool(0,
IrisWorlds = new ArrayList<>(); (engine.isUseVirtualThreads()
IsUnloadAlive = new AtomicBoolean(true); ? Thread.ofVirtual()
IsTrimAlive = new AtomicBoolean(true); : Thread.ofPlatform().priority(engine.getPriority()))
trimActiveAlive = new PrecisionStopwatch(); .name("Iris EngineSVC-", 0)
unloadActiveAlive = new PrecisionStopwatch(); .factory());
trimAlive = new PrecisionStopwatch(); tectonicLimit.set(settings.getTectonicPlateSize());
unloadAlive = new PrecisionStopwatch(); Bukkit.getWorlds().forEach(this::add);
TotalTectonicPlates = new AtomicInteger(); setup();
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;
} }
public void engineStatus() { @Override
boolean trimAlive = trimTicker.isAlive(); public void onDisable() {
boolean unloadAlive = unloadTicker.isAlive(); service.shutdown();
Iris.info("Status:"); updateTicker.interrupt();
Iris.info("- Trim: " + trimAlive); worlds.keySet().forEach(this::remove);
Iris.info("- Unload: " + unloadAlive); worlds.clear();
} }
public static int getTectonicLimit() { public void engineStatus(VolmitSender sender) {
return tectonicLimit.get(); 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 @EventHandler
public void onWorldUnload(WorldUnloadEvent event) { public void onWorldUnload(WorldUnloadEvent event) {
updateWorlds(); remove(event.getWorld());
} }
@EventHandler @EventHandler
public void onWorldLoad(WorldLoadEvent event) { public void onWorldLoad(WorldLoadEvent event) {
updateWorlds(); add(event.getWorld());
} }
@EventHandler private void remove(World world) {
public void onServerBoot(ServerLoadEvent event) { var entry = worlds.remove(world);
isServerLoaded = true; if (entry == null) return;
entry.close();
} }
@EventHandler private void add(World world) {
public void onPluginDisable(PluginDisableEvent event) { var access = IrisToolbelt.access(world);
if (event.getPlugin().equals(Iris.instance)) { if (access == null) return;
isServerShuttingDown = true; worlds.put(world, new Registered(world.getName(), access));
}
} }
public void updateWorlds() { private synchronized void setup() {
for (World world : Bukkit.getWorlds()) { if (updateTicker != null && updateTicker.isAlive())
try { return;
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;
}
};
updateTicker = new Looper() { updateTicker = new Looper() {
@Override @Override
protected long loop() { protected long loop() {
try { try {
TotalQueuedTectonicPlates.set(0); queuedTectonicPlates.set(0);
TotalNotQueuedTectonicPlates.set(0); tectonicPlates.set(0);
TotalTectonicPlates.set(0); loadedChunks.set(0);
for (World world : IrisWorlds) { unloaderAlive.set(0);
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine(); trimmerAlive.set(0);
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload()); totalWorlds.set(0);
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();
}
}
if (!unloadTicker.isAlive()) { double maxDuration = Long.MIN_VALUE;
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!"); double minDuration = Long.MAX_VALUE;
try { for (var entry : worlds.entrySet()) {
UnloadLogic(); var registered = entry.getValue();
} catch (Exception e) { if (registered.closed) continue;
Iris.error("What happened?");
e.printStackTrace();
}
}
}
} catch (Exception e) { totalWorlds.incrementAndGet();
return -1; 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; return 1000;
} }
}; };
updateTicker.start();
} }
public void TrimLogic() {
if (trimTicker == null || !trimTicker.isAlive()) {
trimTicker = new Looper() {
private final Supplier<Engine> supplier = createSupplier();
@Override private final class Registered {
protected long loop() { private final String name;
long start = System.currentTimeMillis(); private final PlatformChunkGenerator access;
trimAlive.reset(); private transient ScheduledFuture<?> trimmer;
try { private transient ScheduledFuture<?> unloader;
Engine engine = supplier.get(); private transient boolean closed;
if (engine != null) {
engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); 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.getMantle().trim(tectonicLimit());
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
Iris.info(C.RED + "EngineSVC: Failed to trim."); Iris.error("EngineSVC: Failed to trim for " + name);
e.printStackTrace(); e.printStackTrace();
return -1; }
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
} }
int size = lastUse.size(); if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); unloader = service.scheduleAtFixedRate(() -> {
if (time <= 0) Engine engine = getEngine();
return 0; if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
return time; return;
}
};
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 { try {
Engine engine = supplier.get();
if (engine != null) {
long unloadStart = System.currentTimeMillis(); long unloadStart = System.currentTimeMillis();
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size()); int count = engine.getMantle().unloadTectonicPlate(tectonicLimit());
if (count > 0) { if (count > 0) {
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
} }
}
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
Iris.info(C.RED + "EngineSVC: Failed to unload."); Iris.error("EngineSVC: Failed to unload for " + name);
e.printStackTrace(); e.printStackTrace();
return -1;
} }
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
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() { private int tectonicLimit() {
AtomicInteger i = new AtomicInteger(); return tectonicLimit.get() / Math.max(worlds.size(), 1);
return () -> {
List<World> worlds = Bukkit.getWorlds();
if (i.get() >= worlds.size()) {
i.set(0);
}
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) { @Synchronized
Engine engine = generator.getEngine(); private void close() {
boolean closed = engine.getMantle().getData().isClosed(); if (closed) return;
if (engine != null && !engine.isStudio() && !closed) { closed = true;
lastUseLock.lock();
lastUse.put(world, System.currentTimeMillis()); if (trimmer != null) {
lastUseLock.unlock(); trimmer.cancel(false);
return engine; trimmer = null;
}
}
}
} catch (Throwable e) {
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
e.printStackTrace();
Iris.reportError(e);
}
return null;
};
} }
@Override if (unloader != null) {
public void onDisable() { unloader.cancel(false);
cacheTicker.interrupt(); unloader = null;
trimTicker.interrupt(); }
unloadTicker.interrupt(); }
lastUse.clear();
@Nullable
private Engine getEngine() {
if (closed) return null;
return access.getEngine();
}
} }
} }

View File

@ -51,6 +51,7 @@ import org.bukkit.util.Vector;
import java.awt.Color; import java.awt.Color;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -80,6 +81,8 @@ public class WandSVC implements IrisService {
try { try {
Location[] f = getCuboid(p); Location[] f = getCuboid(p);
if (f == null || f[0] == null || f[1] == null)
return null;
Cuboid c = new Cuboid(f[0], f[1]); Cuboid c = new Cuboid(f[0], f[1]);
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ()); 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) { public static Location stringToLocation(String s) {
try { try {
String[] f = s.split("\\Q in \\E"); String[] f = s.split("\\Q in \\E");
if (f.length != 2) return null;
String[] g = f[0].split("\\Q,\\E"); 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])); return new Location(Bukkit.getWorld(f[1]), Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2]));
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
@ -357,6 +362,7 @@ public class WandSVC implements IrisService {
try { try {
if ((IrisSettings.get().getWorld().worldEditWandCUI && isHoldingWand(p)) || isWand(p.getInventory().getItemInMainHand())) { if ((IrisSettings.get().getWorld().worldEditWandCUI && isHoldingWand(p)) || isWand(p.getInventory().getItemInMainHand())) {
Location[] d = getCuboid(p); Location[] d = getCuboid(p);
if (d == null || d[0] == null || d[1] == null) return;
new WandSelection(new Cuboid(d[0], d[1]), p).draw(); new WandSelection(new Cuboid(d[0], d[1]), p).draw();
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -1,625 +0,0 @@
package com.volmit.iris.core.tools;
import com.volmit.iris.Iris;
import com.volmit.iris.util.format.C;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
import oshi.software.os.OperatingSystem;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;
import java.util.zip.Deflater;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import static com.google.common.math.LongMath.isPrime;
import static com.volmit.iris.util.misc.getHardware.getCPUModel;
public class IrisBenchmarking {
static String ServerOS;
static String filePath = "benchmark.dat";
static double avgWriteSpeedMBps;
static double avgReadSpeedMBps;
static double highestWriteSpeedMBps;
static double highestReadSpeedMBps;
static double lowestWriteSpeedMBps;
static double lowestReadSpeedMBps;
static double calculateIntegerMath;
static double calculateFloatingPoint;
static double calculatePrimeNumbers;
static double calculateStringSorting;
static double calculateDataEncryption;
static double calculateDataCompression;
static String currentRunning = "None";
static int BenchmarksCompleted = 0;
static int BenchmarksTotal = 7;
static int totalTasks = 10;
static int currentTasks = 0;
static double WindowsCPUCompression;
static double WindowsCPUEncryption;
static double WindowsCPUCSHA1;
static double elapsedTimeNs;
static boolean Winsat = false;
static boolean WindowsDiskSpeed = false;
public static boolean inProgress = false;
static double startTime;
// Good enough for now. . .
public static void runBenchmark() throws InterruptedException {
inProgress = true;
getServerOS();
deleteTestFile(filePath);
AtomicReference<Double> doneCalculateDiskSpeed = new AtomicReference<>((double) 0);
startBenchmarkTimer();
Iris.info("Benchmark Started!");
Iris.warn("Although it may seem momentarily paused, it's actively processing.");
BenchmarksCompleted = 0;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
currentRunning = "calculateDiskSpeed";
progressBar();
if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
WindowsDiskSpeed = true;
WindowsDiskSpeedTest();
} else {
warningFallback();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
doneCalculateDiskSpeed.set(roundToTwoDecimalPlaces(calculateDiskSpeed()));
BenchmarksCompleted++;
}
}).thenRun(() -> {
currentRunning = "WindowsCpuSpeedTest";
progressBar();
if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
Winsat = true;
WindowsCpuSpeedTest();
} else {
Iris.info("Skipping:" + C.BLUE + " Windows System Assessment Tool Benchmarks");
if (!ServerOS.contains("Windows")) {
Iris.info("Required Software:" + C.BLUE + " Windows");
BenchmarksTotal = 6;
}
if (!isRunningAsAdmin()) {
Iris.info(C.RED + "ERROR: " + C.DARK_RED + "Elevated privileges missing");
BenchmarksTotal = 6;
}
}
}).thenRun(() -> {
currentRunning = "calculateIntegerMath";
progressBar();
calculateIntegerMath = roundToTwoDecimalPlaces(calculateIntegerMath());
BenchmarksCompleted++;
}).thenRun(() -> {
currentRunning = "calculateFloatingPoint";
progressBar();
calculateFloatingPoint = roundToTwoDecimalPlaces(calculateFloatingPoint());
BenchmarksCompleted++;
}).thenRun(() -> {
currentRunning = "calculateStringSorting";
progressBar();
calculateStringSorting = roundToTwoDecimalPlaces(calculateStringSorting());
BenchmarksCompleted++;
}).thenRun(() -> {
currentRunning = "calculatePrimeNumbers";
progressBar();
calculatePrimeNumbers = roundToTwoDecimalPlaces(calculatePrimeNumbers());
BenchmarksCompleted++;
}).thenRun(() -> {
currentRunning = "calculateDataEncryption";
progressBar();
calculateDataEncryption = roundToTwoDecimalPlaces(calculateDataEncryption());
BenchmarksCompleted++;
}).thenRun(() -> {
currentRunning = "calculateDataCompression";
progressBar();
calculateDataCompression = roundToTwoDecimalPlaces(calculateDataCompression());
BenchmarksCompleted++;
}).thenRun(() -> {
elapsedTimeNs = stopBenchmarkTimer();
results();
inProgress = false;
});
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public static void progressBar() {
Iris.info("-----------------------------------------------------");
Iris.info("Currently Running: " + C.BLUE + currentRunning);
// Iris.info("Tasks: " + "Current Tasks: " + C.BLUE + currentTasks + C.WHITE + " / " + "Total Tasks: " + C.BLUE + totalTasks);
Iris.info("Benchmarks Completed: " + C.BLUE + BenchmarksCompleted + C.WHITE + " / " + "Total: " + C.BLUE + BenchmarksTotal);
Iris.info("-----------------------------------------------------");
}
public static void results() {
SystemInfo systemInfo = new SystemInfo();
GlobalMemory globalMemory = systemInfo.getHardware().getMemory();
long totalMemoryMB = globalMemory.getTotal() / (1024 * 1024);
long availableMemoryMB = globalMemory.getAvailable() / (1024 * 1024);
long totalPageSize = globalMemory.getPageSize() / (1024 * 1024);
long usedMemoryMB = totalMemoryMB - availableMemoryMB;
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
Iris.info("OS: " + ServerOS);
if (!isRunningAsAdmin() || !ServerOS.contains("Windows")) {
Iris.info(C.GOLD + "For the full results use Windows + Admin Rights..");
}
Iris.info("CPU Model: " + getCPUModel());
Iris.info("CPU Score: " + "WIP");
Iris.info("- Integer Math: " + calculateIntegerMath + " MOps/Sec");
Iris.info("- Floating Point Math: " + calculateFloatingPoint + " MOps/Sec");
Iris.info("- Find Prime Numbers: " + calculatePrimeNumbers + " Primes/Sec");
Iris.info("- Random String Sorting: " + calculateStringSorting + " Thousand Strings/Sec");
Iris.info("- Data Encryption: " + formatDouble(calculateDataEncryption) + " MBytes/Sec");
Iris.info("- Data Compression: " + formatDouble(calculateDataCompression) + " MBytes/Sec");
if (WindowsDiskSpeed) {
//Iris.info("Disk Model: " + getDiskModel());
Iris.info(C.BLUE + "- Running with Windows System Assessment Tool");
Iris.info("- Sequential 64.0 Write: " + C.BLUE + formatDouble(avgWriteSpeedMBps) + " Mbps");
Iris.info("- Sequential 64.0 Read: " + C.BLUE + formatDouble(avgReadSpeedMBps) + " Mbps");
} else {
// Iris.info("Disk Model: " + getDiskModel());
Iris.info(C.GREEN + "- Running in Native Mode");
Iris.info("- Average Write Speed: " + C.GREEN + formatDouble(avgWriteSpeedMBps) + " Mbps");
Iris.info("- Average Read Speed: " + C.GREEN + formatDouble(avgReadSpeedMBps) + " Mbps");
Iris.info("- Highest Write Speed: " + formatDouble(highestWriteSpeedMBps) + " Mbps");
Iris.info("- Highest Read Speed: " + formatDouble(highestReadSpeedMBps) + " Mbps");
Iris.info("- Lowest Write Speed: " + formatDouble(lowestWriteSpeedMBps) + " Mbps");
Iris.info("- Lowest Read Speed: " + formatDouble(lowestReadSpeedMBps) + " Mbps");
}
Iris.info("Ram Usage: ");
Iris.info("- Total Ram: " + totalMemoryMB + " MB");
Iris.info("- Used Ram: " + usedMemoryMB + " MB");
Iris.info("- Total Process Ram: " + C.BLUE + getMaxMemoryUsage() + " MB");
Iris.info("- Total Paging Size: " + totalPageSize + " MB");
if (Winsat) {
Iris.info(C.BLUE + "Windows System Assessment Tool: ");
Iris.info("- CPU LZW Compression:" + C.BLUE + formatDouble(WindowsCPUCompression) + " MB/s");
Iris.info("- CPU AES256 Encryption: " + C.BLUE + formatDouble(WindowsCPUEncryption) + " MB/s");
Iris.info("- CPU SHA1 Hash: " + C.BLUE + formatDouble(WindowsCPUCSHA1) + " MB/s");
Iris.info("Duration: " + roundToTwoDecimalPlaces(elapsedTimeNs) + " Seconds");
}
}
public static long getMaxMemoryUsage() {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
long maxHeapMemory = heapMemoryUsage.getMax();
long maxNonHeapMemory = nonHeapMemoryUsage.getMax();
long maxMemoryUsageMB = (maxHeapMemory + maxNonHeapMemory) / (1024 * 1024);
return maxMemoryUsageMB;
}
public static void getServerOS() {
SystemInfo systemInfo = new SystemInfo();
OperatingSystem os = systemInfo.getOperatingSystem();
ServerOS = os.toString();
}
public static boolean isRunningAsAdmin() {
if (ServerOS.contains("Windows")) {
try {
Process process = Runtime.getRuntime().exec("winsat disk");
process.waitFor();
return process.exitValue() == 0;
} catch (IOException | InterruptedException e) {
// Hmm
}
}
return false;
}
public static void warningFallback() {
Iris.info(C.RED + "Using the " + C.DARK_RED + "FALLBACK" + C.RED + " method due to compatibility issues. ");
Iris.info(C.RED + "Please note that this may result in less accurate results.");
}
private static String formatDouble(double value) {
return String.format("%.2f", value);
}
private static void startBenchmarkTimer() {
startTime = System.nanoTime();
}
private static double stopBenchmarkTimer() {
long endTime = System.nanoTime();
return (endTime - startTime) / 1_000_000_000.0;
}
private static double calculateIntegerMath() {
final int numIterations = 1_000_000_000;
final int numRuns = 30;
double totalMopsPerSec = 0;
for (int run = 0; run < numRuns; run++) {
long startTime = System.nanoTime();
int result = 0;
for (int i = 0; i < numIterations; i++) {
result += i * 2;
result -= i / 2;
result ^= i;
result <<= 1;
result >>= 1;
}
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
totalMopsPerSec += mopsPerSec;
}
double averageMopsPerSec = totalMopsPerSec / numRuns;
return averageMopsPerSec;
}
private static double calculateFloatingPoint() {
long numIterations = 85_000_000;
int numRuns = 30;
double totalMopsPerSec = 0;
for (int run = 0; run < numRuns; run++) {
double result = 0;
long startTime = System.nanoTime();
for (int i = 0; i < numIterations; i++) {
result += Math.sqrt(i) * Math.sin(i) / (i + 1);
}
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
totalMopsPerSec += mopsPerSec;
}
double averageMopsPerSec = totalMopsPerSec / numRuns;
return averageMopsPerSec;
}
private static double calculatePrimeNumbers() {
int primeCount;
long numIterations = 1_000_000;
int numRuns = 30;
double totalMopsPerSec = 0;
for (int run = 0; run < numRuns; run++) {
primeCount = 0;
long startTime = System.nanoTime();
for (int num = 2; primeCount < numIterations; num++) {
if (isPrime(num)) {
primeCount++;
}
}
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double mopsPerSec = (primeCount / elapsedSeconds) / 1_000_000.0;
totalMopsPerSec += mopsPerSec;
}
double averageMopsPerSec = totalMopsPerSec / numRuns;
return averageMopsPerSec;
}
private static double calculateStringSorting() {
int stringCount = 1_000_000;
int stringLength = 100;
int numRuns = 30;
double totalMopsPerSec = 0;
for (int run = 0; run < numRuns; run++) {
List<String> randomStrings = generateRandomStrings(stringCount, stringLength);
long startTime = System.nanoTime();
randomStrings.sort(String::compareTo);
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double mopsPerSec = (stringCount / elapsedSeconds) / 1_000.0;
totalMopsPerSec += mopsPerSec;
}
double averageMopsPerSec = totalMopsPerSec / numRuns;
return averageMopsPerSec;
}
public static double calculateDataEncryption() {
int dataSizeMB = 100;
byte[] dataToEncrypt = generateRandomData(dataSizeMB * 1024 * 1024);
int numRuns = 20;
double totalMBytesPerSec = 0;
for (int run = 0; run < numRuns; run++) {
long startTime = System.nanoTime();
byte[] encryptedData = performEncryption(dataToEncrypt, 1);
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double mbytesPerSec = (dataToEncrypt.length / (1024 * 1024.0)) / elapsedSeconds;
totalMBytesPerSec += mbytesPerSec;
}
double averageMBytesPerSec = totalMBytesPerSec / numRuns;
return averageMBytesPerSec;
}
private static byte[] performEncryption(byte[] data, int numRuns) {
byte[] key = "MyEncryptionKey".getBytes();
byte[] result = Arrays.copyOf(data, data.length);
for (int run = 0; run < numRuns; run++) {
for (int i = 0; i < result.length; i++) {
result[i] ^= key[i % key.length];
}
}
return result;
}
public static double calculateDataCompression() {
int dataSizeMB = 500;
byte[] dataToCompress = generateRandomData(dataSizeMB * 1024 * 1024);
long startTime = System.nanoTime();
byte[] compressedData = performCompression(dataToCompress);
long endTime = System.nanoTime();
double elapsedSeconds = (endTime - startTime) / 1e9;
double mbytesPerSec = (compressedData.length / (1024.0 * 1024.0)) / elapsedSeconds;
return mbytesPerSec;
}
private static byte[] performCompression(byte[] data) {
Deflater deflater = new Deflater();
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
deflater.end();
return outputStream.toByteArray();
}
private static List<String> generateRandomStrings(int count, int length) {
SecureRandom random = new SecureRandom();
List<String> randomStrings = new ArrayList<>();
IntStream.range(0, count).forEach(i -> {
byte[] bytes = new byte[length];
random.nextBytes(bytes);
randomStrings.add(Base64.getEncoder().encodeToString(bytes));
});
return randomStrings;
}
private static byte[] generateRandomData(int size) {
SecureRandom random = new SecureRandom();
byte[] data = new byte[size];
random.nextBytes(data);
return data;
}
private static double roundToTwoDecimalPlaces(double value) {
return Double.parseDouble(String.format("%.2f", value));
}
private static double calculateCPUScore(long elapsedTimeNs) {
return 1.0 / (elapsedTimeNs / 1_000_000.0);
}
public static double calculateDiskSpeed() {
int numRuns = 10;
int fileSizeMB = 1000;
double[] writeSpeeds = new double[numRuns];
double[] readSpeeds = new double[numRuns];
for (int run = 0; run < numRuns; run++) {
long writeStartTime = System.nanoTime();
deleteTestFile(filePath);
createTestFile(filePath, fileSizeMB);
long writeEndTime = System.nanoTime();
long readStartTime = System.nanoTime();
readTestFile(filePath);
long readEndTime = System.nanoTime();
double writeSpeed = calculateDiskSpeedMBps(fileSizeMB, writeStartTime, writeEndTime);
double readSpeed = calculateDiskSpeedMBps(fileSizeMB, readStartTime, readEndTime);
writeSpeeds[run] = writeSpeed;
readSpeeds[run] = readSpeed;
if (run == 0) {
lowestWriteSpeedMBps = writeSpeed;
highestWriteSpeedMBps = writeSpeed;
lowestReadSpeedMBps = readSpeed;
highestReadSpeedMBps = readSpeed;
} else {
if (writeSpeed < lowestWriteSpeedMBps) {
lowestWriteSpeedMBps = writeSpeed;
}
if (writeSpeed > highestWriteSpeedMBps) {
highestWriteSpeedMBps = writeSpeed;
}
if (readSpeed < lowestReadSpeedMBps) {
lowestReadSpeedMBps = readSpeed;
}
if (readSpeed > highestReadSpeedMBps) {
highestReadSpeedMBps = readSpeed;
}
}
}
avgWriteSpeedMBps = calculateAverage(writeSpeeds);
avgReadSpeedMBps = calculateAverage(readSpeeds);
return 2;
}
public static void createTestFile(String filePath, int fileSizeMB) {
try {
File file = new File(filePath);
byte[] data = new byte[1024 * 1024];
Arrays.fill(data, (byte) 0);
FileOutputStream fos = new FileOutputStream(file);
for (int i = 0; i < fileSizeMB; i++) {
fos.write(data);
}
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readTestFile(String filePath) {
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
while (fis.read(buffer) != -1) {
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void deleteTestFile(String filePath) {
File file = new File(filePath);
file.delete();
}
public static double calculateDiskSpeedMBps(int fileSizeMB, long startTime, long endTime) {
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
double writeSpeed = (fileSizeMB / elapsedSeconds);
return writeSpeed;
}
public static double calculateAverage(double[] values) {
double sum = 0;
for (double value : values) {
sum += value;
}
return sum / values.length;
}
public static void WindowsDiskSpeedTest() {
try {
String command = "winsat disk";
Process process = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
Iris.debug(line);
if (line.contains("Disk Sequential 64.0 Read")) {
avgReadSpeedMBps = extractSpeed(line);
} else if (line.contains("Disk Sequential 64.0 Write")) {
avgWriteSpeedMBps = extractSpeed(line);
}
}
process.waitFor();
process.destroy();
Iris.debug("Sequential Read Speed: " + avgReadSpeedMBps + " MB/s");
Iris.debug("Sequential Write Speed: " + avgWriteSpeedMBps + " MB/s");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private static double extractSpeed(String line) {
String[] tokens = line.split("\\s+");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].endsWith("MB/s") && i > 0) {
try {
return Double.parseDouble(tokens[i - 1]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
return 0.0;
}
public static void WindowsCpuSpeedTest() {
try {
String command = "winsat cpuformal";
Process process = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
Iris.debug(line);
if (line.contains("CPU AES256 Encryption")) {
WindowsCPUEncryption = extractCpuInfo(line);
}
if (line.contains("CPU LZW Compression")) {
WindowsCPUCompression = extractCpuInfo(line);
}
if (line.contains("CPU SHA1 Hash")) {
WindowsCPUCSHA1 = extractCpuInfo(line);
}
}
process.waitFor();
process.destroy();
Iris.debug("Winsat Encryption: " + WindowsCPUEncryption + " MB/s");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private static double extractCpuInfo(String line) {
String[] tokens = line.split("\\s+");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].endsWith("MB/s") && i > 0) {
try {
return Double.parseDouble(tokens[i - 1]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
return 0.0;
}
}

View File

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.ServerConfigurator;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
@ -170,7 +171,7 @@ public class IrisCreator {
try { try {
J.sfut(() -> { J.sfut(() -> {
world.set(wc.createWorld()); world.set(INMS.get().createWorld(wc));
}).get(); }).get();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -9,51 +9,43 @@ import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.exceptions.IrisException; import com.volmit.iris.util.exceptions.IrisException;
import com.volmit.iris.util.format.Form; 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.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; 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.Clock;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Collections; import java.util.Collections;
public class IrisPackBenchmarking { public class IrisPackBenchmarking {
@Getter private static final ThreadLocal<IrisPackBenchmarking> instance = new ThreadLocal<>();
public static IrisPackBenchmarking instance;
public static boolean benchmarkInProgress = false;
private final PrecisionStopwatch stopwatch = new PrecisionStopwatch(); private final PrecisionStopwatch stopwatch = new PrecisionStopwatch();
private final IrisDimension dimension; private final IrisDimension dimension;
private final int radius; private final int radius;
private final boolean gui; private final boolean gui;
public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) { public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) {
instance = this;
this.dimension = dimension; this.dimension = dimension;
this.radius = radius; this.radius = radius;
this.gui = gui; this.gui = gui;
runBenchmark(); runBenchmark();
} }
public static IrisPackBenchmarking getInstance() {
return instance.get();
}
private void runBenchmark() { private void runBenchmark() {
Thread.ofVirtual() Thread.ofVirtual()
.name("PackBenchmarking") .name("PackBenchmarking")
.start(() -> { .start(() -> {
Iris.info("Setting up benchmark environment "); Iris.info("Setting up benchmark environment ");
benchmarkInProgress = true; IO.delete(new File(Bukkit.getWorldContainer(), "benchmark"));
File file = new File("benchmark");
if (file.exists()) {
deleteDirectory(file.toPath());
}
createBenchmark(); createBenchmark();
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) { while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
J.sleep(1000); J.sleep(1000);
@ -66,13 +58,9 @@ public class IrisPackBenchmarking {
} }
public boolean getBenchmarkInProgress() {
return benchmarkInProgress;
}
public void finishedBenchmark(KList<Integer> cps) { public void finishedBenchmark(KList<Integer> cps) {
try { try {
String time = Form.duration(stopwatch.getMillis()); String time = Form.duration((long) stopwatch.getMilliseconds());
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine(); Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
Iris.info("-----------------"); Iris.info("-----------------");
Iris.info("Results:"); Iris.info("Results:");
@ -83,11 +71,7 @@ public class IrisPackBenchmarking {
Iris.info(" - Lowest CPS: " + findLowest(cps)); Iris.info(" - Lowest CPS: " + findLowest(cps));
Iris.info("-----------------"); Iris.info("-----------------");
Iris.info("Creating a report.."); Iris.info("Creating a report..");
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks"); File results = Iris.instance.getDataFile("packbenchmarks", dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
profilers.mkdir();
File results = new File(profilers, dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
results.getParentFile().mkdirs();
KMap<String, Double> metrics = engine.getMetrics().pull(); KMap<String, Double> metrics = engine.getMetrics().pull();
try (FileWriter writer = new FileWriter(results)) { try (FileWriter writer = new FileWriter(results)) {
writer.write("-----------------\n"); writer.write("-----------------\n");
@ -143,13 +127,18 @@ public class IrisPackBenchmarking {
} }
private void startBenchmark() { private void startBenchmark() {
try {
instance.set(this);
IrisToolbelt.pregenerate(PregenTask IrisToolbelt.pregenerate(PregenTask
.builder() .builder()
.gui(gui) .gui(gui)
.width(radius) .radiusX(radius)
.height(radius) .radiusZ(radius)
.build(), Bukkit.getWorld("benchmark") .build(), Bukkit.getWorld("benchmark")
); );
} finally {
instance.remove();
}
} }
private double calculateAverage(KList<Integer> list) { private double calculateAverage(KList<Integer> list) {
@ -178,26 +167,4 @@ public class IrisPackBenchmarking {
private int findHighest(KList<Integer> list) { private int findHighest(KList<Integer> list) {
return Collections.max(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;
}
}
} }

View File

@ -24,6 +24,7 @@ import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.PregeneratorMethod; import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.pregenerator.methods.CachedPregenMethod;
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod; import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
@ -141,7 +142,18 @@ public class IrisToolbelt {
* @return the pregenerator job (already started) * @return the pregenerator job (already started)
*/ */
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) { public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
return new PregeneratorJob(task, method, engine); return pregenerate(task, method, engine, IrisSettings.get().getPregen().useCacheByDefault);
}
/**
* Start a pregenerator task
*
* @param task the scheduled task
* @param method the method to execute the task
* @return the pregenerator job (already started)
*/
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine, boolean cached) {
return new PregeneratorJob(task, cached && engine != null ? new CachedPregenMethod(method, engine.getWorld().name()) : method, engine);
} }
/** /**

View File

@ -32,8 +32,6 @@ import sun.misc.Unsafe;
import java.io.File; import java.io.File;
public class IrisWorldCreator { public class IrisWorldCreator {
public static final WorldType IRIS;
private String name; private String name;
private boolean studio = false; private boolean studio = false;
private String dimensionName = null; private String dimensionName = null;
@ -85,7 +83,6 @@ public class IrisWorldCreator {
return new WorldCreator(name) return new WorldCreator(name)
.type(IRIS)
.environment(findEnvironment()) .environment(findEnvironment())
.generateStructures(true) .generateStructures(true)
.generator(g).seed(seed); .generator(g).seed(seed);
@ -104,17 +101,4 @@ public class IrisWorldCreator {
this.studio = studio; this.studio = studio;
return this; return this;
} }
static {
try {
var unsafe = new WrappedField<Unsafe, Unsafe>(Unsafe.class, "theUnsafe").get();
var iris = (WorldType) unsafe.allocateInstance(WorldType.class);
unsafe.putIntVolatile(iris, unsafe.objectFieldOffset(Enum.class.getDeclaredField("ordinal")), 0);
unsafe.putObjectVolatile(iris, unsafe.objectFieldOffset(Enum.class.getDeclaredField("name")), "IRIS");
IRIS = iris;
} catch (Throwable e) {
throw new ExceptionInInitializerError(e);
}
}
} }

View File

@ -96,7 +96,6 @@ public class IrisEngine implements Engine {
private EngineExecutionEnvironment execution; private EngineExecutionEnvironment execution;
private EngineWorldManager worldManager; private EngineWorldManager worldManager;
private volatile int parallelism; private volatile int parallelism;
private volatile int minHeight;
private boolean failing; private boolean failing;
private boolean closed; private boolean closed;
private int cacheId; private int cacheId;
@ -129,7 +128,6 @@ public class IrisEngine implements Engine {
getData().setEngine(this); getData().setEngine(this);
getData().loadPrefetch(this); getData().loadPrefetch(this);
Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed()); Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
minHeight = 0;
failing = false; failing = false;
closed = false; closed = false;
art = J.ar(this::tickRandomPlayer, 0); art = J.ar(this::tickRandomPlayer, 0);
@ -180,7 +178,10 @@ public class IrisEngine implements Engine {
File[] roots = getData().getLoaders() File[] roots = getData().getLoaders()
.values() .values()
.stream() .stream()
.map(ResourceLoader::getRoot) .map(ResourceLoader::getFolderName)
.map(n -> new File(getData().getDataFolder(), n))
.filter(File::exists)
.filter(File::isDirectory)
.toArray(File[]::new); .toArray(File[]::new);
hash32.complete(IO.hashRecursive(roots)); hash32.complete(IO.hashRecursive(roots));
}); });
@ -472,7 +473,7 @@ public class IrisEngine implements Engine {
getEngineData().getStatistics().generatedChunk(); getEngineData().getStatistics().generatedChunk();
try { try {
PrecisionStopwatch p = PrecisionStopwatch.start(); 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)) { if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {

View File

@ -55,6 +55,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -367,7 +368,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private void spawn(IrisPosition pos, IrisEntitySpawn i) { private void spawn(IrisPosition pos, IrisEntitySpawn i) {
IrisSpawner ref = i.getReferenceSpawner(); IrisSpawner ref = i.getReferenceSpawner();
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ())) if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4))
return; return;
int s = i.spawn(getEngine(), pos, RNG.r); int s = i.spawn(getEngine(), pos, RNG.r);
@ -422,9 +423,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
return; return;
} }
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; energy += 0.3;
fixEnergy(); fixEnergy();
getEngine().cleanupMantleChunk(e.getX(), e.getZ()); getEngine().cleanupMantleChunk(x, z);
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
if (generated) { if (generated) {
//INMS.get().injectBiomesFromMantle(e, getMantle()); //INMS.get().injectBiomesFromMantle(e, getMantle());

View File

@ -75,9 +75,9 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color; import java.awt.Color;
import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -140,7 +140,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getTarget().getWorld().minHeight(); return getTarget().getWorld().minHeight();
} }
void setMinHeight(int min); default void setMinHeight(int min) {
getTarget().getWorld().minHeight(min);
}
@BlockCoordinates @BlockCoordinates
default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException { default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException {
@ -287,11 +289,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return; return;
} }
var chunk = mantle.getChunk(c); var chunk = mantle.getChunk(c).use();
if (chunk.isFlagged(MantleFlag.ETCHED)) return; try {
chunk.flag(MantleFlag.ETCHED, true);
Semaphore semaphore = new Semaphore(3); Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> { chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> { mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
int betterY = y + getWorld().minHeight(); int betterY = y + getWorld().minHeight();
@ -353,10 +354,14 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class); mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds()); getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(0, 20)))); }, RNG.r.i(0, 20))));
});
try { try {
semaphore.acquire(3); semaphore.acquire(3);
} catch (InterruptedException ignored) {} } catch (InterruptedException ignored) {}
} finally {
chunk.release();
}
} }
private static Runnable run(Semaphore semaphore, Runnable runnable) { private static Runnable run(Semaphore semaphore, Runnable runnable) {
@ -453,14 +458,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
} }
} }
for (int i = 0; i < 4; i++) { for (int i = nitems.length; i > 1; i--) {
try { int j = rng.nextInt(i);
Arrays.parallelSort(nitems, (a, b) -> rng.nextInt()); ItemStack tmp = nitems[i - 1];
break; nitems[i - 1] = nitems[j];
} catch (Throwable e) { nitems[j] = tmp;
Iris.reportError(e);
}
} }
inventory.setContents(nitems); inventory.setContents(nitems);
@ -852,6 +854,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ()); return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
} }
@Nullable
@BlockCoordinates
default Position2 getNearestStronghold(Position2 pos) {
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
if (p.isEmpty()) return null;
Position2 pr = null;
double d = Double.MAX_VALUE;
for (Position2 i : p) {
double dx = i.distance(pos);
if (dx < d) {
d = dx;
pr = i;
}
}
return pr;
}
default void gotoBiome(IrisBiome biome, Player player, boolean teleport) { default void gotoBiome(IrisBiome biome, Player player, boolean teleport) {
Set<String> regionKeys = getDimension() Set<String> regionKeys = getDimension()
.getAllRegions(this).stream() .getAllRegions(this).stream()
@ -872,31 +893,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) { default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
if (s.getLoadKey().equals(getDimension().getStronghold())) { if (s.getLoadKey().equals(getDimension().getStronghold())) {
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle()); Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
if (pr == null) {
if (p.isEmpty()) {
player.sendMessage(C.GOLD + "No strongholds in world."); player.sendMessage(C.GOLD + "No strongholds in world.");
} } else {
Position2 px = new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ());
Position2 pr = null;
double d = Double.MAX_VALUE;
Iris.debug("Ps: " + p.size());
for (Position2 i : p) {
Iris.debug("- " + i.getX() + " " + i.getZ());
}
for (Position2 i : p) {
double dx = i.distance(px);
if (dx < d) {
d = dx;
pr = i;
}
}
if (pr != null) {
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ()); Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
J.s(() -> player.teleport(ll)); J.s(() -> player.teleport(ll));
} }

View File

@ -97,51 +97,6 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
} }
} }
@EventHandler
public void onItemUse(PlayerInteractEvent e) {
if (e.getItem() == null || e.getHand() != EquipmentSlot.HAND) {
return;
}
if (e.getAction() == Action.LEFT_CLICK_BLOCK || e.getAction() == Action.LEFT_CLICK_AIR) {
return;
}
if (e.getPlayer().getWorld().equals(getTarget().getWorld().realWorld()) && e.getItem().getType() == Material.ENDER_EYE) {
if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.END_PORTAL_FRAME) {
return;
}
KList<Position2> positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getMantle());
if (positions.isEmpty()) {
return;
}
Position2 playerPos = new Position2(e.getPlayer().getLocation().getBlockX(), e.getPlayer().getLocation().getBlockZ());
Position2 pr = positions.get(0);
double d = pr.distance(playerPos);
for (Position2 pos : positions) {
double distance = pos.distance(playerPos);
if (distance < d) {
d = distance;
pr = pos;
}
}
if (e.getPlayer().getGameMode() != GameMode.CREATIVE) {
if (e.getItem().getAmount() > 1) {
e.getPlayer().getInventory().getItemInMainHand().setAmount(e.getItem().getAmount() - 1);
} else {
e.getPlayer().getInventory().setItemInMainHand(null);
}
}
EnderSignal eye = e.getPlayer().getWorld().spawn(e.getPlayer().getLocation().clone().add(0, 0.5F, 0), EnderSignal.class);
eye.setTargetLocation(new Location(e.getPlayer().getWorld(), pr.getX(), 40, pr.getZ()));
eye.getWorld().playSound(eye, Sound.ENTITY_ENDER_EYE_LAUNCH, 1, 1);
Iris.debug("ESignal: " + eye.getTargetLocation().getBlockX() + " " + eye.getTargetLocation().getBlockX());
}
}
@EventHandler @EventHandler
public void on(WorldUnloadEvent e) { public void on(WorldUnloadEvent e) {
if (e.getWorld().equals(getTarget().getWorld().realWorld())) { if (e.getWorld().equals(getTarget().getWorld().realWorld())) {

View File

@ -47,9 +47,7 @@ public class EnginePlayer {
} }
public void tick() { public void tick() {
sample(); if (sample() || !IrisSettings.get().getWorld().isEffectSystem())
if (!IrisSettings.get().getWorld().isEffectSystem())
return; return;
J.a(() -> { J.a(() -> {
@ -81,22 +79,22 @@ public class EnginePlayer {
return M.ms() - lastSample; return M.ms() - lastSample;
} }
public void sample() { public boolean sample() {
Location current = player.getLocation().clone();
if (current.getWorld() != engine.getWorld().realWorld())
return true;
try { try {
if (ticksSinceLastSample() > 55 && player.getLocation().distanceSquared(lastLocation) > 9 * 9) { if (ticksSinceLastSample() > 55 && current.distanceSquared(lastLocation) > 9 * 9) {
lastLocation = player.getLocation().clone(); lastLocation = current;
lastSample = M.ms(); lastSample = M.ms();
sampleBiomeRegion(); biome = engine.getBiome(current);
region = engine.getRegion(current);
} }
return false;
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
} }
} return true;
private void sampleBiomeRegion() {
Location l = player.getLocation();
biome = engine.getBiome(l);
region = engine.getRegion(l);
} }
} }

View File

@ -289,23 +289,25 @@ public interface EngineMantle extends IObjectPlacer {
} }
default void cleanupChunk(int x, int z) { default void cleanupChunk(int x, int z) {
if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) { if (!isCovered(x, z)) return;
getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> { MantleChunk chunk = getMantle().getChunk(x, z).use();
getMantle().deleteChunkSlice(x, z, BlockData.class); try {
getMantle().deleteChunkSlice(x, z, String.class); chunk.raiseFlag(MantleFlag.CLEANED, () -> {
getMantle().deleteChunkSlice(x, z, MatterCavern.class); chunk.deleteSlices(BlockData.class);
getMantle().deleteChunkSlice(x, z, MatterFluidBody.class); chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class);
chunk.deleteSlices(MatterFluidBody.class);
}); });
} finally {
chunk.release();
} }
} }
default long getToUnload(){ default long getUnloadRegionCount() {
return getMantle().getToUnload().size(); return getMantle().getUnloadRegionCount();
} }
default long getNotQueuedLoadedRegions(){
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size(); default double getAdjustedIdleDuration() {
} return getMantle().getAdjustedIdleDuration();
default double getTectonicDuration(){
return getMantle().getAdjustedIdleDuration().get();
} }
} }

View File

@ -60,8 +60,9 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
this.x = x; this.x = x;
this.z = z; this.z = z;
for (int i = -radius; i <= radius; i++) { int r = radius / 4;
for (int j = -radius; j <= radius; j++) { 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()); 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 if (cx >= this.x - radius && cx <= this.x + radius
&& cz >= this.z - radius && cz <= this.z + 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) { if (chunk == null) {
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); 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 matter = chunk.getOrCreate(y >> 4);
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); 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 @Override
public void close() { public void close() {
cachedChunks.values().removeIf(c -> { var iterator = cachedChunks.values().iterator();
c.release(); while (iterator.hasNext()) {
return true; iterator.next().release();
}); iterator.remove();
}
} }
} }

View File

@ -58,21 +58,21 @@ public class MantleCarvingComponent extends IrisMantleComponent {
@ChunkCoordinates @ChunkCoordinates
private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) { 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() { private int computeRadius() {
var dimension = getDimension(); var dimension = getDimension();
int max = 0; 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)) { 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)) { 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; return max;

View File

@ -43,7 +43,6 @@ import org.bukkit.block.data.BlockData;
public class IrisCarveModifier extends EngineAssignedModifier<BlockData> { public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
private final RNG rng; private final RNG rng;
private final BlockData AIR = Material.CAVE_AIR.createBlockData(); private final BlockData AIR = Material.CAVE_AIR.createBlockData();
private final BlockData WATER = Material.WATER.createBlockData();
private final BlockData LAVA = Material.LAVA.createBlockData(); private final BlockData LAVA = Material.LAVA.createBlockData();
private final IrisDecorantActuator decorant; private final IrisDecorantActuator decorant;
@ -103,7 +102,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
} }
if (c.isWater()) { if (c.isWater()) {
output.set(rx, yy, rz, WATER); output.set(rx, yy, rz, context.getFluid().get(rx, rz));
} else if (c.isLava()) { } else if (c.isLava()) {
output.set(rx, yy, rz, LAVA); output.set(rx, yy, rz, LAVA);
} else { } else {

View File

@ -19,6 +19,7 @@
package com.volmit.iris.engine.object; package com.volmit.iris.engine.object;
import com.volmit.iris.Iris; 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.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
@ -34,8 +35,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import java.util.Map; import java.util.Map;
@ -202,6 +203,14 @@ public class IrisBlockData extends IrisRegistrant {
public TileData tryGetTile(IrisData data) { 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. //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(); 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()) if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty())
return null; return null;
return new TileData(type, this.tileData); return new TileData(type, this.tileData);

View File

@ -61,21 +61,25 @@ public class IrisCarving {
@BlockCoordinates @BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
doCarving(writer, rng, engine, x, y, z, -1); doCarving(writer, rng, engine, x, y, z, depth, -1);
} }
@BlockCoordinates @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()) { if (caves.isNotEmpty()) {
for (IrisCavePlacer i : caves) { 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()) { if (ravines.isNotEmpty()) {
for (IrisRavinePlacer i : ravines) { 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 max = 0;
int nextRecursion = recursion + 1;
for (IrisCavePlacer i : caves) { 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) { 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()) { if (elipsoids.isNotEmpty()) {

View File

@ -66,10 +66,10 @@ public class IrisCave extends IrisRegistrant {
} }
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { 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()); double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> { 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()); int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight());
for (IrisPosition i : points) { 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); MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
@ -108,7 +108,7 @@ public class IrisCave extends IrisRegistrant {
} }
public int getMaxSize(IrisData data) { public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data); return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
} }
} }

View File

@ -50,6 +50,10 @@ public class IrisCavePlacer implements IRare {
@Desc("The cave to place") @Desc("The cave to place")
@RegistryListResource(IrisCave.class) @RegistryListResource(IrisCave.class)
private String cave; 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") @Desc("If set to true, this cave is allowed to break the surface")
private boolean breakSurface = true; 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.") @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) { 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()) { if (fail.get()) {
return; return;
} }
@ -92,18 +96,18 @@ public class IrisCavePlacer implements IRare {
} }
try { 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) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
fail.set(true); fail.set(true);
} }
} }
public int getSize(IrisData data) { public int getSize(IrisData data, int depth) {
IrisCave cave = getRealCave(data); IrisCave cave = getRealCave(data);
if (cave != null) { if (cave != null) {
return cave.getMaxSize(data); return cave.getMaxSize(data, depth);
} }
return 32; return 32;

View File

@ -0,0 +1,66 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.misc.ServerProperties;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
import java.util.List;
import static com.volmit.iris.Iris.instance;
public class IrisContextInjector implements Listener {
@Getter
private static boolean missingDimensionTypes = false;
private AutoClosing autoClosing = null;
public IrisContextInjector() {
if (!Bukkit.getWorlds().isEmpty()) return;
String levelName = ServerProperties.LEVEL_NAME;
List<String> irisWorlds = irisWorlds();
boolean overworld = irisWorlds.contains(levelName);
boolean nether = irisWorlds.contains(levelName + "_nether");
boolean end = irisWorlds.contains(levelName + "_end");
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
missingDimensionTypes = true;
return;
}
if (overworld || nether || end) {
autoClosing = INMS.get().injectUncached(overworld, nether, end);
}
instance.registerListener(this);
}
@EventHandler(priority = EventPriority.LOWEST)
public void on(WorldInitEvent event) {
if (autoClosing != null) {
autoClosing.close();
autoClosing = null;
}
instance.unregisterListener(this);
}
private List<String> irisWorlds() {
var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
ConfigurationSection section = config.getConfigurationSection("worlds");
if (section == null) return List.of();
return section.getKeys(false)
.stream()
.filter(k -> section.getString(k + ".generator", "").startsWith("Iris"))
.toList();
}
}

View File

@ -19,6 +19,7 @@
package com.volmit.iris.engine.object; package com.volmit.iris.engine.object;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.ServerConfigurator.DimensionHeight; import com.volmit.iris.core.ServerConfigurator.DimensionHeight;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
@ -27,6 +28,7 @@ import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.data.DataProvider;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
@ -377,60 +379,35 @@ public class IrisDimension extends IrisRegistrant {
return landBiomeStyle; return landBiomeStyle;
} }
public boolean installDataPack(IDataFixer fixer, DataProvider data, File datapacks, DimensionHeight height) { public void installBiomes(IDataFixer fixer, DataProvider data, KList<File> folders, KSet<String> biomes) {
boolean write = false; getAllBiomes(data)
boolean changed = false; .stream()
.filter(IrisBiome::isCustom)
IO.delete(new File(datapacks, "iris/data/" + getLoadKey().toLowerCase())); .map(IrisBiome::getCustomDerivitives)
.flatMap(KList::stream)
for (IrisBiome i : getAllBiomes(data)) { .parallel()
if (i.isCustom()) { .forEach(j -> {
write = true; String json = j.generateJson(fixer);
synchronized (biomes) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) { if (!biomes.add(j.getId())) {
File output = new File(datapacks, "iris/data/" + getLoadKey().toLowerCase() + "/worldgen/biome/" + j.getId() + ".json"); Iris.verbose("Duplicate Data Pack Biome: " + getLoadKey() + "/" + j.getId());
return;
if (!output.exists()) {
changed = true;
} }
}
for (File datapacks : folders) {
File output = new File(datapacks, "iris/data/" + getLoadKey().toLowerCase() + "/worldgen/biome/" + j.getId() + ".json");
Iris.verbose(" Installing Data Pack Biome: " + output.getPath()); Iris.verbose(" Installing Data Pack Biome: " + output.getPath());
output.getParentFile().mkdirs(); output.getParentFile().mkdirs();
try { try {
IO.writeAll(output, j.generateJson(fixer)); IO.writeAll(output, json);
} catch (IOException e) { } catch (IOException e) {
Iris.reportError(e); Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }
} }
} });
}
Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\"");
changed = writeDimensionType(changed, datapacks, height);
Iris.verbose(" Installing Data Pack World Preset: \"minecraft:iris\"");
changed = writeWorldPreset(changed, datapacks, fixer);
if (write) {
File mcm = new File(datapacks, "iris/pack.mcmeta");
try {
IO.writeAll(mcm, """
{
"pack": {
"description": "Iris Data Pack. This pack contains all installed Iris Packs' resources.",
"pack_format": {}
}
}
""".replace("{}", INMS.get().getDataVersion().getPackFormat() + ""));
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
Iris.verbose(" Installing Data Pack MCMeta: " + mcm.getPath());
}
return changed;
} }
@Override @Override
@ -448,56 +425,55 @@ public class IrisDimension extends IrisRegistrant {
} }
public boolean writeDimensionType(boolean changed, File datapacks, DimensionHeight height) { public static void writeShared(KList<File> folders, DimensionHeight height) {
File dimTypeOverworld = new File(datapacks, "iris/data/iris/dimension_type/overworld.json"); Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\"");
if (!dimTypeOverworld.exists()) for (File datapacks : folders) {
changed = true; write(datapacks, "overworld", height.overworldType());
dimTypeOverworld.getParentFile().mkdirs(); write(datapacks, "the_nether", height.netherType());
write(datapacks, "the_end", height.endType());
}
String raw = """
{
"pack": {
"description": "Iris Data Pack. This pack contains all installed Iris Packs' resources.",
"pack_format": {}
}
}
""".replace("{}", INMS.get().getDataVersion().getPackFormat() + "");
for (File datapacks : folders) {
File mcm = new File(datapacks, "iris/pack.mcmeta");
try { try {
IO.writeAll(dimTypeOverworld, height.overworldType()); IO.writeAll(mcm, raw);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
Iris.verbose(" Installing Data Pack MCMeta: " + mcm.getPath());
}
}
private static void write(File datapacks, String type, String json) {
File dimType = new File(datapacks, "iris/data/iris/dimension_type/" + type + ".json");
File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json");
dimType.getParentFile().mkdirs();
try {
IO.writeAll(dimType, json);
} catch (IOException e) { } catch (IOException e) {
Iris.reportError(e); Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
File dimTypeNether = new File(datapacks, "iris/data/iris/dimension_type/the_nether.json"); dimTypeVanilla.getParentFile().mkdirs();
if (!dimTypeNether.exists())
changed = true;
dimTypeNether.getParentFile().mkdirs();
try { try {
IO.writeAll(dimTypeNether, height.netherType()); IO.writeAll(dimTypeVanilla, json);
} catch (IOException e) { } catch (IOException e) {
Iris.reportError(e); Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }
}
File dimTypeEnd = new File(datapacks, "iris/data/iris/dimension_type/the_end.json");
if (!dimTypeEnd.exists())
changed = true;
dimTypeEnd.getParentFile().mkdirs();
try {
IO.writeAll(dimTypeEnd, height.endType());
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
return changed;
}
public boolean writeWorldPreset(boolean changed, File datapacks, IDataFixer fixer) {
File worldPreset = new File(datapacks, "iris/data/minecraft/worldgen/world_preset/iris.json");
if (!worldPreset.exists())
changed = true;
try {
IO.writeAll(worldPreset, fixer.createPreset());
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
return changed;
} }
} }

View File

@ -28,7 +28,6 @@ import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterMarker; import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.matter.slices.MarkerMatter; import com.volmit.iris.util.matter.slices.MarkerMatter;
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -38,9 +37,6 @@ import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.BoundingBox;
@Snippet("entity-spawn") @Snippet("entity-spawn")
@Accessors(chain = true) @Accessors(chain = true)
@ -116,8 +112,8 @@ public class IrisEntitySpawn implements IRare {
World world = gen.getWorld().realWorld(); World world = gen.getWorld().realWorld();
if (spawns > 0) { if (spawns > 0) {
if (referenceMarker != null) { if (referenceMarker != null && referenceMarker.shouldExhaust()) {
gen.getMantle().getMantle().remove(c.getX(), c.getY(), c.getZ(), MatterMarker.class); gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class);
} }
for (int id = 0; id < spawns; id++) { for (int id = 0; id < spawns; id++) {

View File

@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant {
private boolean emptyAbove = true; 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.") @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() { public boolean shouldExhaust() {
return RNG.r.chance(exhaustionChance); return exhaustionChance > RNG.r.nextDouble();
} }
@Override @Override

View File

@ -93,10 +93,10 @@ public class IrisRavine extends IrisRegistrant {
} }
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { 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) -> { KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> {
}); });
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData()); 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 width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ()));
int surface = (int) Math.round(rsurface - depth * 0.45); 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--) { for (int i = surface + depth; i >= surface; i--) {
if (i % ribThickness == 0) { if (i % ribThickness == 0) {
@ -184,7 +184,7 @@ public class IrisRavine extends IrisRegistrant {
} }
public int getMaxSize(IrisData data) { public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data); return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
} }
} }

View File

@ -50,16 +50,20 @@ public class IrisRavinePlacer implements IRare {
@Desc("The ravine to place") @Desc("The ravine to place")
@RegistryListResource(IrisRavine.class) @RegistryListResource(IrisRavine.class)
private String ravine; private String ravine;
@MinNumber(1)
@MaxNumber(256)
@Desc("The maximum recursion depth")
private int maxRecursion = 100;
public IrisRavine getRealRavine(IrisData data) { public IrisRavine getRealRavine(IrisData data) {
return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine())); return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine()));
} }
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) { 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()) { if (fail.get()) {
return; return;
} }
@ -80,14 +84,14 @@ public class IrisRavinePlacer implements IRare {
try { try {
int xx = x + rng.nextInt(15); int xx = x + rng.nextInt(15);
int zz = z + 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) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
fail.set(true); fail.set(true);
} }
} }
public int getSize(IrisData data) { public int getSize(IrisData data, int depth) {
return getRealRavine(data).getMaxSize(data); return getRealRavine(data).getMaxSize(data, depth);
} }
} }

View File

@ -21,6 +21,7 @@ package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.data.chunk.TerrainChunk;
@ -39,6 +40,7 @@ import com.volmit.iris.util.io.ReactiveFolder;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import io.papermc.lib.PaperLib;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Setter; import lombok.Setter;
@ -48,6 +50,7 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent; import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
@ -84,12 +87,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio; private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0); private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>(); private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private Engine engine; private volatile Engine engine;
private Looper hotloader; private volatile Looper hotloader;
private StudioMode lastMode; private volatile StudioMode lastMode;
private DummyBiomeProvider dummyBiomeProvider; private volatile DummyBiomeProvider dummyBiomeProvider;
@Setter @Setter
private StudioGenerator studioGenerator; private volatile StudioGenerator studioGenerator;
private boolean initialized = false; private boolean initialized = false;
@ -108,25 +111,13 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
} }
private static Field getField(Class clazz, String fieldName) @EventHandler(priority = EventPriority.LOWEST)
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
public void onWorldInit(WorldInitEvent event) { public void onWorldInit(WorldInitEvent event) {
try { try {
if (initialized || !world.name().equals(event.getWorld().getName())) if (initialized || !world.name().equals(event.getWorld().getName()))
return; return;
AutoClosing.closeContext();
INMS.get().removeCustomDimensions(event.getWorld());
world.setRawWorldSeed(event.getWorld().getSeed()); world.setRawWorldSeed(event.getWorld().getSeed());
Engine engine = getEngine(event.getWorld()); Engine engine = getEngine(event.getWorld());
if (engine == null) { if (engine == null) {
@ -154,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() { private void setupEngine() {
IrisData data = IrisData.get(dataLocation); IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
@ -297,7 +302,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
hotloader.interrupt(); hotloader.interrupt();
} }
getEngine().close(); final Engine engine = getEngine();
if (engine != null && !engine.isClosed())
engine.close();
folder.clear(); folder.clear();
populators.clear(); populators.clear();

View File

@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils;
*/ */
public class AtomicAverage { public class AtomicAverage {
protected final AtomicDoubleArray values; protected final AtomicDoubleArray values;
protected int cursor; protected transient int cursor;
private double average; private transient double average;
private double lastSum; private transient double lastSum;
private boolean dirty; private transient boolean dirty;
private boolean brandNew; private transient boolean brandNew;
/** /**
* Create an average holder * Create an average holder
@ -57,7 +57,7 @@ public class AtomicAverage {
* *
* @param i the value * @param i the value
*/ */
public void put(double i) { public synchronized void put(double i) {
try { try {
dirty = true; dirty = true;

View File

@ -26,6 +26,8 @@ import com.volmit.iris.util.math.RNG;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
public class KList<T> extends ArrayList<T> implements List<T> { public class KList<T> extends ArrayList<T> implements List<T> {
@ -65,6 +67,10 @@ public class KList<T> extends ArrayList<T> implements List<T> {
return s; return s;
} }
public static <T> Collector<T, ?, KList<T>> collector() {
return Collectors.toCollection(KList::new);
}
public static KList<String> asStringList(List<?> oo) { public static KList<String> asStringList(List<?> oo) {
KList<String> s = new KList<String>(); KList<String> s = new KList<String>();

View File

@ -23,11 +23,9 @@ import com.volmit.iris.util.function.Consumer2;
import com.volmit.iris.util.function.Consumer3; import com.volmit.iris.util.function.Consumer3;
import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.Queue;
import java.util.Collections; import java.util.*;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
@ -373,6 +371,20 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
return g; 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 * Create a keypair queue
* *

View File

@ -18,29 +18,67 @@
package com.volmit.iris.util.collection; package com.volmit.iris.util.collection;
import java.util.Collection; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
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 static final long serialVersionUID = 1L;
private final ConcurrentHashMap<T, Boolean> map;
public KSet() { public KSet() {
super(); map = new ConcurrentHashMap<>();
} }
public KSet(Collection<? extends T> c) { public KSet(Collection<? extends T> c) {
super(c); this();
addAll(c);
} }
public KSet(int initialCapacity, float loadFactor) { public KSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor); map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
} }
public KSet(int initialCapacity) { 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() { public KSet<T> copy() {
return new KSet<T>(this); return new KSet<>(this);
} }
} }

View File

@ -87,4 +87,21 @@ public class IrisContext {
public IrisComplex getComplex() { public IrisComplex getComplex() {
return engine.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()));
}
} }

View File

@ -160,7 +160,8 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter {
} }
J.aBukkit(() -> { J.aBukkit(() -> {
if (!call(new VolmitSender(sender), args)) { var volmit = new VolmitSender(sender);
if (!call(volmit, args)) {
if (IrisSettings.get().getGeneral().isCommandSounds()) { if (IrisSettings.get().getGeneral().isCommandSounds()) {
if (sender instanceof Player) { 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 { } else {
if (IrisSettings.get().getGeneral().isCommandSounds()) { if (IrisSettings.get().getGeneral().isCommandSounds()) {
if (sender instanceof Player) { if (sender instanceof Player) {

View File

@ -376,6 +376,28 @@ public enum C {
return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2); 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) { public static String aura(String s, int hrad, int srad, int vrad) {
return aura(s, hrad, srad, vrad, 0.3D); return aura(s, hrad, srad, vrad, 0.3D);
} }

View File

@ -18,11 +18,19 @@
package com.volmit.iris.util.io; 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.Iris;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import org.apache.commons.io.function.IOConsumer;
import org.apache.commons.io.function.IOFunction;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.DigestInputStream; import java.security.DigestInputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -134,8 +142,7 @@ public class IO {
continue; continue;
} }
try (var fin = new FileInputStream(file)) { try (var din = new CheckedInputStream(readDeterministic(file), crc)) {
var din = new CheckedInputStream(fin, crc);
fullTransfer(din, new VoidOutputStream(), 8192); fullTransfer(din, new VoidOutputStream(), 8192);
} catch (IOException e) { } catch (IOException e) {
Iris.reportError(e); Iris.reportError(e);
@ -152,10 +159,43 @@ public class IO {
return 0; 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) { public static String hash(File b) {
try { try {
MessageDigest d = MessageDigest.getInstance("SHA-256"); 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); fullTransfer(din, new VoidOutputStream(), 8192);
din.close(); din.close();
return bytesToHex(din.getMessageDigest().digest()); return bytesToHex(din.getMessageDigest().digest());
@ -1602,4 +1642,19 @@ public class IO {
int ch2 = input2.read(); int ch2 = input2.read();
return (ch2 == -1); return (ch2 == -1);
} }
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();
}
}
} }

View File

@ -31,16 +31,22 @@ public class JarScanner {
private final KSet<Class<?>> classes; private final KSet<Class<?>> classes;
private final File jar; private final File jar;
private final String superPackage; private final String superPackage;
private final boolean report;
/** /**
* Create a scanner * Create a scanner
* *
* @param jar the path to the jar * @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.jar = jar;
this.classes = new KSet<>(); this.classes = new KSet<>();
this.superPackage = superPackage; this.superPackage = superPackage;
this.report = report;
}
public JarScanner(File jar, String superPackage) {
this(jar, superPackage, true);
} }
/** /**
@ -65,7 +71,8 @@ public class JarScanner {
try { try {
Class<?> clazz = Class.forName(c); Class<?> clazz = Class.forName(c);
classes.add(clazz); classes.add(clazz);
} catch (ClassNotFoundException e) { } catch (Throwable e) {
if (!report) continue;
Iris.reportError(e); Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -21,20 +21,20 @@ package com.volmit.iris.util.mantle;
import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.MantleWriter; 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.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer4; 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.math.M;
import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice; import com.volmit.iris.util.matter.MatterSlice;
@ -51,8 +51,6 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; 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. * 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 { public class Mantle {
private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true");
private final File dataFolder; private final File dataFolder;
@Getter @Getter
private final int worldHeight; private final int worldHeight;
private final Map<Long, Long> lastUse; private final Map<Long, Long> lastUse;
@Getter
private final Map<Long, TectonicPlate> loadedRegions; private final Map<Long, TectonicPlate> loadedRegions;
private final HyperLock hyperLock; private final HyperLock hyperLock;
private final AtomicBoolean closed; private final AtomicBoolean closed;
private final MultiBurst ioBurst; private final MultiBurst ioBurst;
private final AtomicBoolean ioTrim; private final AtomicBoolean ioTrim;
private final AtomicBoolean ioTectonicUnload; private final AtomicBoolean ioTectonicUnload;
private final AtomicDouble adjustedIdleDuration;
private final KSet<Long> toUnload;
/** /**
* Create a new mantle * Create a new mantle
@ -87,10 +85,11 @@ public class Mantle {
this.worldHeight = worldHeight; this.worldHeight = worldHeight;
this.ioTrim = new AtomicBoolean(false); this.ioTrim = new AtomicBoolean(false);
this.ioTectonicUnload = new AtomicBoolean(false); this.ioTectonicUnload = new AtomicBoolean(false);
dataFolder.mkdirs();
loadedRegions = new KMap<>(); loadedRegions = new KMap<>();
lastUse = new KMap<>(); lastUse = new KMap<>();
ioBurst = MultiBurst.burst; ioBurst = MultiBurst.burst;
adjustedIdleDuration = new AtomicDouble(0);
toUnload = new KSet<>();
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
} }
@ -103,7 +102,7 @@ public class Mantle {
* @return the file * @return the file
*/ */
public static File fileForRegion(File folder, int x, int z) { 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 * @param key the region key
* @return the file * @return the file
*/ */
public static File fileForRegion(File folder, Long key) { public static File fileForRegion(File folder, Long key, boolean convert) {
File f = new File(folder, "p." + key + ".ttp.lz4b"); File f = oldFileForRegion(folder, key);
if (!f.getParentFile().exists()) { File fv = new File(folder, "pv." + key + ".ttp.lz4b");
f.getParentFile().mkdirs(); if (f.exists() && !fv.exists() && convert)
}
return f; return f;
if (!fv.getParentFile().exists()) {
fv.getParentFile().mkdirs();
}
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 @RegionCoordinates
public boolean hasTectonicPlate(int x, int z) { public boolean hasTectonicPlate(int x, int z) {
Long k = key(x, 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() { public synchronized void close() {
Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
if (closed.get()) { if (closed.getAndSet(true)) {
return; return;
} }
closed.set(true); hyperLock.disable();
BurstExecutor b = ioBurst.burst(loadedRegions.size()); BurstExecutor b = ioBurst.burst(toUnload.size());
for (Long i : loadedRegions.keySet()) { loadedRegions.forEach((i, plate) -> b.queue(() -> {
b.queue(() -> {
try { try {
loadedRegions.get(i).write(fileForRegion(dataFolder, i)); plate.close();
} catch (IOException e) { 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(); e.printStackTrace();
} }
}); }));
} loadedRegions.clear();
try { try {
b.complete(); b.complete();
@ -376,7 +394,7 @@ public class Mantle {
Iris.reportError(e); Iris.reportError(e);
} }
loadedRegions.clear(); IO.delete(new File(dataFolder, ".tmp"));
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
} }
@ -392,16 +410,6 @@ public class Mantle {
return numberOfEntries * bytesPerEntry; 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 * Save & unload regions that have not been used for more than the
* specified amount of milliseconds * specified amount of milliseconds
@ -414,93 +422,81 @@ public class Mantle {
} }
adjustedIdleDuration.set(baseIdleDuration); adjustedIdleDuration.set(baseIdleDuration);
if (loadedRegions != null) {
if (loadedRegions.size() > tectonicLimit) { if (loadedRegions.size() > tectonicLimit) {
// todo update this correctly and maybe do something when its above a 100% // 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)); adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
} }
}
ioTrim.set(true); ioTrim.set(true);
unloadLock.lock();
try { try {
if (lastUse != null && IrisEngineSVC.instance != null) { double adjustedIdleDuration = this.adjustedIdleDuration.get();
if (!lastUse.isEmpty()) { Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0));
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
for (long i : new ArrayList<>(lastUse.keySet())) { if (lastUse.isEmpty()) return;
double finalAdjustedIdleDuration = adjustedIdleDuration.get(); double unloadTime = M.ms() - adjustedIdleDuration;
hyperLock.withLong(i, () -> { for (long id : lastUse.keySet()) {
Long lastUseTime = lastUse.get(i); hyperLock.withLong(id, () -> {
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) { Long lastUseTime = lastUse.get(id);
toUnload.add(i); if (lastUseTime != null && lastUseTime < unloadTime) {
toUnload.add(id);
Iris.debug("Tectonic Region added to unload"); Iris.debug("Tectonic Region added to unload");
IrisEngineSVC.instance.trimActiveAlive.reset();
} }
}); });
} }
}
}
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
} finally { } finally {
ioTrim.set(false); ioTrim.set(false);
unloadLock.unlock();
} }
} }
public synchronized int unloadTectonicPlate(int tectonicLimit) { public synchronized int unloadTectonicPlate(int tectonicLimit) {
AtomicInteger i = new AtomicInteger(); if (closed.get()) {
unloadLock.lock(); throw new RuntimeException("The Mantle is closed");
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;
} }
burst.queue(() -> AtomicInteger i = new AtomicInteger();
hyperLock.withLong(id, () -> { 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); TectonicPlate m = loadedRegions.get(id);
if (m != null) { if (m == null) {
if (m.inUse()) { Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); toUnload.remove(id);
if (disableClear) toUnload.remove(id);
lastUse.put(id, M.ms());
return; return;
} }
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 { try {
m.write(fileForRegion(dataFolder, id)); m.write(fileForRegion(dataFolder, id, false));
oldFileForRegion(dataFolder, id).delete();
loadedRegions.remove(id); loadedRegions.remove(id);
lastUse.remove(id); lastUse.remove(id);
if (disableClear) toUnload.remove(id); toUnload.remove(id);
i.incrementAndGet(); i.incrementAndGet();
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
IrisEngineSVC.instance.unloadActiveAlive.reset();
} catch (IOException e) { } catch (IOException e) {
Iris.reportError(e); Iris.reportError(e);
} }
}
})); }));
} }
burst.complete(); burst.complete();
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
if (burst != null)
burst.complete(); burst.complete();
} finally { } finally {
unloadLock.unlock(); ioTectonicUnload.set(false);
ioTectonicUnload.set(true);
}
return i.get();
} }
return i.get(); return i.get();
} }
@ -516,7 +512,7 @@ public class Mantle {
*/ */
@RegionCoordinates @RegionCoordinates
private TectonicPlate get(int x, int z) { private TectonicPlate get(int x, int z) {
if (ioTrim.get()) { if (ioTrim.get() || ioTectonicUnload.get()) {
try { try {
return getSafe(x, z).get(); return getSafe(x, z).get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -576,7 +572,7 @@ public class Mantle {
if (file.exists()) { if (file.exists()) {
try { try {
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); 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) { 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); 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(); 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) { public <T> void set(int x, int y, int z, MatterSlice<T> slice) {
if (slice.isEmpty()) { if (slice.isEmpty()) {
return; return;

View File

@ -19,6 +19,7 @@
package com.volmit.iris.util.mantle; package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris; 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.ChunkCoordinates;
import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.CountingDataInputStream;
@ -30,7 +31,8 @@ import lombok.Getter;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; 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.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.atomic.AtomicReferenceArray;
@ -45,7 +47,8 @@ public class MantleChunk {
private final int z; private final int z;
private final AtomicIntegerArray flags; private final AtomicIntegerArray flags;
private final AtomicReferenceArray<Matter> sections; 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 * Create a mantle chunk
@ -72,11 +75,12 @@ public class MantleChunk {
* @throws IOException shit happens * @throws IOException shit happens
* @throws ClassNotFoundException 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()); this(sectionHeight, din.readByte(), din.readByte());
int s = 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); flags.set(i, din.readBoolean() ? 1 : 0);
} }
@ -85,6 +89,10 @@ public class MantleChunk {
long size = din.readInt(); long size = din.readInt();
if (size == 0) continue; if (size == 0) continue;
long start = din.count(); long start = din.count();
if (i >= sectionHeight) {
din.skipTo(start + size);
continue;
}
try { try {
sections.set(i, Matter.readDin(din)); 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() { public boolean inUse() {
return ref.get() > 0; return ref.availablePermits() < Integer.MAX_VALUE;
} }
public MantleChunk use() { public MantleChunk use() {
ref.incrementAndGet(); if (closed.get()) throw new IllegalStateException("Chunk is closed!");
ref.acquireUninterruptibly();
return this; return this;
} }
public void release() { public void release() {
ref.decrementAndGet(); ref.release();
} }
public void flag(MantleFlag flag, boolean f) { public void flag(MantleFlag flag, boolean f) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
flags.set(flag.ordinal(), f ? 1 : 0); flags.set(flag.ordinal(), f ? 1 : 0);
} }
@ -201,6 +216,7 @@ public class MantleChunk {
dos.writeByte(x); dos.writeByte(x);
dos.writeByte(z); dos.writeByte(z);
dos.writeByte(sections.length()); dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), dos);
for (int i = 0; i < flags.length(); i++) { for (int i = 0; i < flags.length(); i++) {
dos.writeBoolean(flags.get(i) == 1); dos.writeBoolean(flags.get(i) == 1);

View File

@ -19,13 +19,15 @@
package com.volmit.iris.util.mantle; package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.EnginePanic;
import com.volmit.iris.engine.data.cache.Cache; 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.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter; import lombok.Getter;
import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockInputStream;
@ -44,7 +46,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
* Tectonic Plates are fully atomic & thread safe * Tectonic Plates are fully atomic & thread safe
*/ */
public class TectonicPlate { 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 int sectionHeight;
private final AtomicReferenceArray<MantleChunk> chunks; private final AtomicReferenceArray<MantleChunk> chunks;
@ -74,11 +78,12 @@ public class TectonicPlate {
* @param din the data input * @param din the data input
* @throws IOException shit happens yo * @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()); this(worldHeight, din.readInt(), din.readInt());
if (!din.markSupported()) if (!din.markSupported())
throw new IOException("Mark not supported!"); throw new IOException("Mark not supported!");
int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING;
for (int i = 0; i < chunks.length(); i++) { for (int i = 0; i < chunks.length(); i++) {
long size = din.readInt(); long size = din.readInt();
if (size == 0) continue; if (size == 0) continue;
@ -86,7 +91,7 @@ public class TectonicPlate {
try { try {
Iris.addPanic("read-chunk", "Chunk[" + i + "]"); Iris.addPanic("read-chunk", "Chunk[" + i + "]");
chunks.set(i, new MantleChunk(sectionHeight, din)); chunks.set(i, new MantleChunk(v, sectionHeight, din));
EnginePanic.saveLast(); EnginePanic.saveLast();
} catch (Throwable e) { } catch (Throwable e) {
long end = start + size; 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)) { try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock(); fc.lock();
@ -111,10 +116,10 @@ public class TectonicPlate {
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
BufferedInputStream bis = new BufferedInputStream(lz4); BufferedInputStream bis = new BufferedInputStream(lz4);
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) { try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
return new TectonicPlate(worldHeight, din); return new TectonicPlate(worldHeight, din, versioned);
} }
} finally { } finally {
if (errors.remove(Thread.currentThread())) { if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin"); File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock(); fc.lock();
@ -124,6 +129,7 @@ public class TectonicPlate {
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }
} }
errors.remove();
} }
} }
@ -136,6 +142,15 @@ public class TectonicPlate {
return false; 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) * 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 { public void write(File file) throws IOException {
PrecisionStopwatch p = PrecisionStopwatch.start(); PrecisionStopwatch p = PrecisionStopwatch.start();
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) { IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write);
fc.lock(); Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
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));
}
}
} }
/** /**
@ -228,6 +236,7 @@ public class TectonicPlate {
public void write(DataOutputStream dos) throws IOException { public void write(DataOutputStream dos) throws IOException {
dos.writeInt(x); dos.writeInt(x);
dos.writeInt(z); dos.writeInt(z);
Varint.writeUnsignedVarInt(CURRENT, dos);
var bytes = new ByteArrayOutputStream(8192); var bytes = new ByteArrayOutputStream(8192);
var sub = new DataOutputStream(bytes); var sub = new DataOutputStream(bytes);
@ -249,6 +258,6 @@ public class TectonicPlate {
} }
public static void addError() { public static void addError() {
errors.add(Thread.currentThread()); errors.set(true);
} }
} }

View File

@ -63,7 +63,7 @@ public class Spiraler {
public void next() { public void next() {
if ((-sizeX / 2 <= x) && (x <= sizeX / 2) && (-sizeZ / 2 <= z) && (z <= sizeZ / 2)) { if ((-sizeX / 2 <= x) && (x <= sizeX / 2) && (-sizeZ / 2 <= z) && (z <= sizeZ / 2)) {
spiraled.on(x + ox, z + ox); spiraled.on(x + ox, z + oz);
} }
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) { if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) {

View File

@ -154,16 +154,17 @@ public interface Matter {
matter.putSlice(type, slice); matter.putSlice(type, slice);
} catch (Throwable e) { } catch (Throwable e) {
long end = start + size; long end = start + size;
if (!(e instanceof ClassNotFoundException)) {
Iris.error("Failed to read matter slice, skipping it."); Iris.error("Failed to read matter slice, skipping it.");
Iris.addPanic("read.byte.range", start + " " + end); Iris.addPanic("read.byte.range", start + " " + end);
Iris.addPanic("read.byte.current", din.count() + ""); Iris.addPanic("read.byte.current", din.count() + "");
Iris.reportError(e); Iris.reportError(e);
e.printStackTrace(); e.printStackTrace();
Iris.panic(); Iris.panic();
din.skipTo(end);
TectonicPlate.addError(); TectonicPlate.addError();
} }
din.skipTo(end);
}
} }
return matter; return matter;

View File

@ -18,10 +18,10 @@
package com.volmit.iris.util.matter.slices; 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.IrisCustomData;
import com.volmit.iris.util.data.palette.Palette; import com.volmit.iris.util.data.palette.Palette;
import com.volmit.iris.util.matter.Sliced; import com.volmit.iris.util.matter.Sliced;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -63,6 +63,6 @@ public class BlockMatter extends RawMatter<BlockData> {
@Override @Override
public BlockData readNode(DataInputStream din) throws IOException { public BlockData readNode(DataInputStream din) throws IOException {
return Bukkit.createBlockData(din.readUTF()); return B.get(din.readUTF());
} }
} }

View File

@ -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];
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,44 @@
package com.volmit.iris.util.misc;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class ServerProperties {
public static final Properties DATA = new Properties();
public static final File SERVER_PROPERTIES;
public static final File BUKKIT_YML;
public static final String LEVEL_NAME;
static {
String[] args = ProcessHandle.current()
.info()
.arguments()
.orElse(new String[0]);
String propertiesPath = "server.properties";
String bukkitYml = "bukkit.yml";
String levelName = null;
for (int i = 0; i < args.length - 1; i++) {
switch (args[i]) {
case "-c", "--config" -> propertiesPath = args[i + 1];
case "-b", "--bukkit-settings" -> bukkitYml = args[i + 1];
case "-w", "--level-name", "--world" -> levelName = args[i + 1];
}
}
SERVER_PROPERTIES = new File(propertiesPath);
BUKKIT_YML = new File(bukkitYml);
try (FileInputStream in = new FileInputStream(SERVER_PROPERTIES)){
DATA.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (levelName != null) LEVEL_NAME = levelName;
else LEVEL_NAME = DATA.getProperty("level-name", "world");
}
}

View File

@ -143,5 +143,6 @@ public class HyperLock {
public void disable() { public void disable() {
enabled = false; enabled = false;
locks.values().forEach(ReentrantLock::lock);
} }
} }

View File

@ -24,12 +24,15 @@ import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
public class MultiBurst { public class MultiBurst implements ExecutorService {
private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000);
public static final MultiBurst burst = new MultiBurst(); public static final MultiBurst burst = new MultiBurst();
private final AtomicLong last; private final AtomicLong last;
private final String name; private final String name;
@ -144,14 +147,92 @@ public class MultiBurst {
return getService().submit(o); return getService().submit(o);
} }
@Override
public void shutdown() {
close();
}
@NotNull
@Override
public List<Runnable> shutdownNow() {
close();
return List.of();
}
@Override
public boolean isShutdown() {
return service == null || service.isShutdown();
}
@Override
public boolean isTerminated() {
return service == null || service.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return service == null || service.awaitTermination(timeout, unit);
}
@Override
public void execute(@NotNull Runnable command) {
getService().execute(command);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Callable<T> task) {
return getService().submit(task);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Runnable task, T result) {
return getService().submit(task, result);
}
@NotNull
@Override
public Future<?> submit(@NotNull Runnable task) {
return getService().submit(task);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException {
return getService().invokeAll(tasks);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return getService().invokeAll(tasks, timeout, unit);
}
@NotNull
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return getService().invokeAny(tasks);
}
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return getService().invokeAny(tasks, timeout, unit);
}
public void close() { public void close() {
if (service != null) { if (service != null) {
close(service);
}
}
public static void close(ExecutorService service) {
service.shutdown(); service.shutdown();
PrecisionStopwatch p = PrecisionStopwatch.start(); PrecisionStopwatch p = PrecisionStopwatch.start();
try { try {
while (!service.awaitTermination(1, TimeUnit.SECONDS)) { while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
Iris.info("Still waiting to shutdown burster..."); Iris.info("Still waiting to shutdown burster...");
if (p.getMilliseconds() > 7000) { if (p.getMilliseconds() > TIMEOUT) {
Iris.warn("Forcing Shutdown..."); Iris.warn("Forcing Shutdown...");
try { try {
@ -169,4 +250,3 @@ public class MultiBurst {
} }
} }
} }
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
};
}
}

View File

@ -264,7 +264,7 @@ public class VolmitSender implements CommandSender {
private Component createNoPrefixComponent(String message) { private Component createNoPrefixComponent(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(message)); 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); String t = C.translateAlternateColorCodes('&', message);
@ -273,13 +273,13 @@ public class VolmitSender implements CommandSender {
} }
private Component createNoPrefixComponentNoProcessing(String message) { 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) { private Component createComponent(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); 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); String t = C.translateAlternateColorCodes('&', getTag() + message);
@ -290,11 +290,11 @@ public class VolmitSender implements CommandSender {
private Component createComponentRaw(String message) { private Component createComponentRaw(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); 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); 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) { public <T> void showWaiting(String passive, CompletableFuture<T> f) {

View File

@ -36,7 +36,6 @@ public abstract class Looper extends Thread {
//noinspection BusyWait //noinspection BusyWait
Thread.sleep(m); Thread.sleep(m);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Iris.reportError(e);
break; break;
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);

View File

@ -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);
}
}

View File

@ -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();
}
}

View 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();
}
}
}

View File

@ -23,5 +23,5 @@ libraries:
commands: commands:
iris: iris:
aliases: [ ir, irs ] aliases: [ ir, irs ]
api-version: '${apiversion}' api-version: '${apiVersion}'
hotload-dependencies: false hotload-dependencies: false

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

41
gradlew vendored
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +80,11 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -133,22 +131,29 @@ location of your Java installation."
fi fi
else else
JAVACMD=java 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 Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) 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 ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | 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" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # 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 -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ 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. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

31
gradlew.bat vendored
View File

@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList; import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.MobCategory;
@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override @Override
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) { 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()) if (engine.getDimension().isDisableExplorerMaps())
return null; return null;

View File

@ -2,8 +2,10 @@ package com.volmit.iris.core.nms.v1_20_R1;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; 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.container.BiomeColor;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
@ -20,27 +22,36 @@ import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.BlockPos; import lombok.SneakyThrows;
import net.minecraft.core.Holder; import net.minecraft.core.*;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; 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.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block; 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.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
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.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -73,11 +84,10 @@ import java.io.DataOutputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Iterator; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
@ -85,9 +95,11 @@ public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); 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<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -568,6 +580,9 @@ public class NMSBinding implements INMSBinding {
public void inject(long seed, Engine engine, World world) { public void inject(long seed, Engine engine, World world) {
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().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());
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
} }
@ -628,4 +643,105 @@ public class NMSBinding implements INMSBinding {
} }
} }
} }
@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.registryOrThrow(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().registryOrThrow(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().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(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.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(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.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), Lifecycle.stable());
}
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.lifecycle(value);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return new ResourceLocation("iris", key.location().getPath());
}
} }

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