9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-26 18:49:06 +00:00

Compare commits

..

144 Commits

Author SHA1 Message Date
Julian Krings
a9891e819a v+ 2025-07-12 12:51:04 +02:00
Julian Krings
02c13a9391 Merge pull request #1211 from VolmitSoftware/dev
3.7.0
2025-07-12 12:42:38 +02:00
Julian Krings
f6590c26e7 fix download failing for dimensions with snippets 2025-07-11 13:07:20 +02:00
Julian Krings
64bb81626c update overworld pack 2025-07-11 12:54:16 +02:00
Julian Krings
343dc429d5 fix null pointer when failing to load a dimension 2025-07-10 19:20:46 +02:00
Julian Krings
94c5782490 move loading message to debug 2025-07-09 18:49:55 +02:00
Julian Krings
cc49b0f540 hopefully fix eta 2025-07-09 17:37:58 +02:00
Julian Krings
0edaeeec99 massive cleanup of maven repos and update dependencies 2025-07-08 23:40:10 +02:00
Julian Krings
a0543bbbf2 relocate slimjar 2025-07-08 19:59:45 +02:00
Julian Krings
0b1af51227 add option to disable specific mantle components for testing 2025-07-08 19:52:09 +02:00
Julian Krings
03c5998c02 generate a dummy region for standalone focus biomes 2025-07-08 19:08:12 +02:00
Julian Krings
6b193f695a switch to slimjar 2025-07-08 18:46:59 +02:00
Julian Krings
2e2ea8f1e4 use paper plugin loader to avoid maven central warning 2025-07-08 09:19:57 +02:00
Julian Krings
f9888d19a5 whoops forgot to update publish task 2025-07-08 00:24:03 +02:00
Julian Krings
fbc5cee300 if it still doesn't work, idk what will 2025-07-08 00:22:31 +02:00
Julian Krings
12777bc3f0 does this shit work now?? 2025-07-07 23:59:42 +02:00
Julian Krings
9324b1b5c0 fix api file generation 2025-07-07 23:42:30 +02:00
Julian Krings
2ecb555619 add publishing for the api artifact 2025-07-07 23:37:37 +02:00
Julian Krings
20c7891c2f add api generation task 2025-07-07 22:54:35 +02:00
Julian Krings
cb93e78242 fix safeguard error 2025-07-07 13:18:31 +02:00
Julian Krings
c9ed4519a8 remove multiverse core incompatibility warning (#1212) 2025-07-06 20:00:06 +02:00
Julian Krings
86d986dfbc fix deposits spawning in columns 2025-07-06 19:40:14 +02:00
Julian Krings
54402faea8 fix automatic deepslate variant replacement 2025-07-04 13:18:28 +02:00
Julian Krings
77b4253624 fix sentry safeguard info 2025-07-04 12:34:20 +02:00
Julian Krings
44af23ba2e add check for new dimension types 2025-07-04 00:06:55 +02:00
Julian Krings
4e8079e431 use ticket queue for pregen by default 2025-07-04 00:01:16 +02:00
Julian Krings
39f65d02bf 1.21.6/7 Support (#1210) 2025-07-03 23:58:31 +02:00
Julian Krings
8c025a9ba2 clear snippet lists on hotload 2025-07-03 23:45:27 +02:00
Julian Krings
7119fa8ef7 Merge pull request #1207 from VolmitSoftware/feat/byte_buddy
Feat/byte buddy
2025-07-03 23:24:30 +02:00
Julian Krings
6f0b2b6bba initialize generators for isolated focus biomes / regions 2025-06-27 12:21:59 +02:00
Julian Krings
0ae1334a57 fix snippet tab completion 2025-06-27 12:20:25 +02:00
Julian Krings
5d28563b7c implement generate surface for ores 2025-06-24 19:43:46 +02:00
Julian Krings
3677931114 improve snippet finder 2025-06-23 15:51:53 +02:00
Julian Krings
e38dae0a32 fix required properties for json schemas 2025-06-23 15:51:29 +02:00
Julian Krings
d537459c5a Merge branch 'dev' into feat/byte_buddy
# Conflicts:
#	build.gradle.kts
#	core/build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
2025-06-23 14:24:43 +02:00
Julian Krings
67398174bb cache iris worlds in a map for datapack generation 2025-06-23 12:25:39 +02:00
Julian Krings
0a0f77ff58 fix splash being shown twice 2025-06-23 12:25:38 +02:00
Julian Krings
e5cb4d82a3 Add KGenerators support (#1209) 2025-06-21 12:05:56 +02:00
Julian Krings
9d44ac0b47 fix caves being non-deterministic 2025-06-21 12:04:39 +02:00
Julian Krings
7f6d65a13e fix worm ignoring the breakSurface option 2025-06-21 12:01:06 +02:00
Julian Krings
ad720f4aa2 fix worm using xStyle for y and z 2025-06-21 11:51:49 +02:00
Julian Krings
80548f753c isolate java agent to prevent class loader issues 2025-06-21 11:48:58 +02:00
Julian Krings
c9c8a9e412 fix null pointers in pack trim method 2025-06-16 22:53:56 +02:00
Julian Krings
d048c073ac disable trim for fallback pack download 2025-06-16 22:53:11 +02:00
Julian Krings
da2dd42e28 invert suppressed error reporting for dev enviroments 2025-06-16 16:59:24 +02:00
Julian Krings
88360ef772 Merge branch 'dev' into feat/byte_buddy
# Conflicts:
#	build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
2025-06-15 23:00:39 +02:00
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
b62ac875d5 cleanup gradle 2025-06-10 16:48:00 +02:00
Julian Krings
4c3f95b0e1 Merge branch 'dev' into feat/byte_buddy
# Conflicts:
#	build.gradle
#	core/src/main/java/com/volmit/iris/Iris.java
2025-06-10 13:33:56 +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
cc27e87376 fix world height decreases deleting the whole chunk 2025-06-05 20:34:25 +02:00
Julian Krings
abb1d9cd62 add bytebuddy binding for 1.21.5 2025-06-05 20:26:16 +02:00
Julian Krings
e0ad029c3d Merge branch 'dev' into feat/byte_buddy
# Conflicts:
#	build.gradle
#	core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java
2025-06-05 19:27:01 +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
f68600464b remove redundant method from NMSBinding1X 2025-05-25 18:12:48 +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
Julian Krings
6724b0f4c5 minor cleanup and improved safeguard for missing dimension types 2025-04-25 21:18:33 +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
Julian Krings
2008975a8a expose most of the dimension type options 2025-04-17 17:30:29 +02:00
Julian Krings
aad1d3815a Merge branch 'dev' into feat/byte_buddy 2025-04-16 23:18:49 +02:00
Julian Krings
86c64f99e9 filter out non iris pack directories in allPacks stream 2025-04-16 23:01:57 +02:00
Julian Krings
6577f4a5de rough safeguard for missing dimension type 2025-04-16 22:02:42 +02:00
Julian Krings
3415e7c7af implement complete world height isolation 2025-04-16 21:52:30 +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
Julian Krings
9c151abac7 replace world load context injection with bytecode injections 2025-04-10 16:26:58 +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
162 changed files with 8540 additions and 5859 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.5-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.4 - 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)

280
build.gradle.kts Normal file
View File

@@ -0,0 +1,280 @@
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`
alias(libs.plugins.shadow)
alias(libs.plugins.sentry)
alias(libs.plugins.download)
alias(libs.plugins.runPaper)
}
group = "com.volmit"
version = "3.7.0-1.20.1-1.21.7"
apply<ApiGenerator>()
// 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_R5" to "1.21.7-R0.1-SNAPSHOT",
"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(rootProject.libs.annotations)
compileOnly(rootProject.libs.byteBuddy.core)
}
}
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.suppressReporting", !errorReporting)
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
}
}
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) })
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
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()
}
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://jitpack.io") // EcoItems, score
maven("https://repo.nexomc.com/releases/") // nexo
maven("https://maven.devs.beer/") // itemsadder
maven("https://repo.extendedclip.com/releases/") // placeholderapi
maven("https://mvn.lumine.io/repository/maven-public/") // mythic
maven("https://nexus.phoenixdevt.fr/repository/maven-public/") //MMOItems
maven("https://repo.onarandombox.com/content/groups/public/") //Multiverse Core
}
dependencies {
// Provided or Classpath
compileOnly(rootProject.libs.lombok)
annotationProcessor(rootProject.libs.lombok)
}
/**
* 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" }
}
}

11
buildSrc/build.gradle.kts Normal file
View File

@@ -0,0 +1,11 @@
plugins {
kotlin("jvm") version "2.0.20"
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.ow2.asm:asm:9.8")
}

View File

@@ -0,0 +1,121 @@
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.jvm.tasks.Jar
import org.objectweb.asm.*
import java.io.File
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
class ApiGenerator : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
plugins.apply("maven-publish")
val task = tasks.register("irisApi", GenerateApiTask::class.java)
extensions.findByType(PublishingExtension::class.java)!!.apply {
repositories.maven {
it.name = "deployDir"
it.url = targetDirectory.toURI()
}
publications.create("maven", MavenPublication::class.java) {
it.groupId = name
it.version = version.toString()
it.artifact(task)
}
}
}
}
abstract class GenerateApiTask : DefaultTask() {
init {
group = "iris"
dependsOn("jar")
finalizedBy("publishMavenPublicationToDeployDirRepository")
doLast {
logger.lifecycle("The API is located at ${outputFile.absolutePath}")
}
}
@InputFile
val inputFile: File = project.tasks
.named("jar", Jar::class.java)
.get()
.archiveFile
.get()
.asFile
@OutputFile
val outputFile: File = project.targetDirectory.resolve(inputFile.name)
@TaskAction
fun generate() {
JarFile(inputFile).use { jar ->
JarOutputStream(outputFile.apply { parentFile?.mkdirs() }.outputStream()).use { out ->
jar.stream()
.parallel()
.filter { !it.isDirectory }
.filter { it.name.endsWith(".class") }
.forEach {
val bytes = jar.getInputStream(it).use { input ->
val writer = ClassWriter(ClassWriter.COMPUTE_MAXS)
val visitor = MethodClearingVisitor(writer)
ClassReader(input).accept(visitor, 0)
writer.toByteArray()
}
synchronized(out) {
out.putNextEntry(it)
out.write(bytes)
out.closeEntry()
}
}
}
}
}
}
val Project.targetDirectory: File get() {
val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile
return File(dir)
}
private class MethodClearingVisitor(
cv: ClassVisitor
) : ClassVisitor(Opcodes.ASM9, cv) {
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
) = ExceptionThrowingMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions))
}
private class ExceptionThrowingMethodVisitor(
mv: MethodVisitor
) : MethodVisitor(Opcodes.ASM9, mv) {
override fun visitCode() {
if (mv == null) return
mv.visitCode()
mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException")
mv.visitInsn(Opcodes.DUP)
mv.visitLdcInsn("Only API")
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"java/lang/IllegalStateException",
"<init>", "(Ljava/lang/String;)V", false
)
mv.visitInsn(Opcodes.ATHROW)
mv.visitMaxs(0, 0)
mv.visitEnd()
}
}

View File

@@ -0,0 +1,12 @@
plugins {
java
}
tasks.jar {
manifest.attributes(
"Agent-Class" to "com.volmit.iris.util.agent.Installer",
"Premain-Class" to "com.volmit.iris.util.agent.Installer",
"Can-Redefine-Classes" to true,
"Can-Retransform-Classes" to true
)
}

View File

@@ -0,0 +1,29 @@
package com.volmit.iris.util.agent;
import java.lang.instrument.Instrumentation;
public class Installer {
private static volatile Instrumentation instrumentation;
public static Instrumentation getInstrumentation() {
Instrumentation instrumentation = Installer.instrumentation;
if (instrumentation == null) {
throw new IllegalStateException("The agent is not loaded or this method is not called via the system class loader");
}
return instrumentation;
}
public static void premain(String arguments, Instrumentation instrumentation) {
doMain(instrumentation);
}
public static void agentmain(String arguments, Instrumentation instrumentation) {
doMain(instrumentation);
}
private static synchronized void doMain(Instrumentation instrumentation) {
if (Installer.instrumentation != null)
return;
Installer.instrumentation = instrumentation;
}
}

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

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

@@ -0,0 +1,158 @@
import io.github.slimjar.func.slimjar
import io.github.slimjar.resolver.data.Mirror
import java.net.URI
/*
* 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`
alias(libs.plugins.shadow)
alias(libs.plugins.sentry)
alias(libs.plugins.slimjar)
}
val apiVersion = "1.19"
val main = "com.volmit.iris.Iris"
val lib = "com.volmit.iris.util"
/**
* 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(libs.spigot)
compileOnly(libs.log4j.api)
compileOnly(libs.log4j.core)
// Third Party Integrations
compileOnly(libs.nexo)
compileOnly(libs.itemsadder)
compileOnly(libs.placeholderApi)
compileOnly(libs.score)
compileOnly(libs.mmoitems)
compileOnly(libs.ecoitems)
compileOnly(libs.mythic)
compileOnly(libs.mythicChrucible)
compileOnly(libs.kgenerators) {
isTransitive = false
}
compileOnly(libs.multiverseCore)
// Shaded
implementation(slimjar())
// Dynamically Loaded
slim(libs.paralithic)
slim(libs.paperlib)
slim(libs.adventure.api)
slim(libs.adventure.minimessage)
slim(libs.adventure.platform)
slim(libs.bstats)
slim(libs.sentry)
slim(libs.commons.io)
slim(libs.commons.lang)
slim(libs.commons.lang3)
slim(libs.oshi)
slim(libs.lz4)
slim(libs.fastutil)
slim(libs.lru)
slim(libs.zip)
slim(libs.gson)
slim(libs.asm)
slim(libs.bsf)
slim(libs.rhino)
slim(libs.caffeine)
slim(libs.byteBuddy.core)
slim(libs.byteBuddy.agent)
}
java {
disableAutoTargetJvm()
}
sentry {
autoInstallation.enabled = false
includeSourceContext = true
org = "volmit-software"
projectName = "iris"
authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN")
}
slimJar {
mirrors = listOf(Mirror(
URI.create("https://maven-central.storage-download.googleapis.com/maven2").toURL(),
URI.create("https://repo.maven.apache.org/maven2/").toURL()
))
relocate("com.dfsek.paralithic", "$lib.paralithic")
relocate("io.papermc.lib", "$lib.paper")
relocate("net.kyori", "$lib.kyori")
relocate("org.bstats", "$lib.metrics")
relocate("io.sentry", "$lib.sentry")
}
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()
//minimize()
relocate("io.github.slimjar", "$lib.slimjar")
}
}
/**
* Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly.
*/
afterEvaluate {
layout.buildDirectory.file("resources/main/plugin.yml").get().asFile.delete()
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,8 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.misc.SlimJar;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.VolmitSender;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -44,6 +46,8 @@ public class IrisSettings {
private IrisSettingsStudio studio = new IrisSettingsStudio();
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
private IrisSettingsPregen pregen = new IrisSettingsPregen();
private IrisSettingsSentry sentry = new IrisSettingsSentry();
public static int getThreadCount(int c) {
return switch (c) {
@@ -83,6 +87,7 @@ public class IrisSettings {
Iris.error("Configuration Error in settings.json! " + ee.getClass().getSimpleName() + ": " + ee.getMessage());
}
}
SlimJar.debug(settings.general.debug);
return settings;
}
@@ -129,6 +134,7 @@ public class IrisSettings {
public boolean markerEntitySpawningSystem = true;
public boolean effectSystem = true;
public boolean worldEditWandCUI = true;
public boolean globalPregenCache = false;
}
@Data
@@ -141,14 +147,33 @@ public class IrisSettings {
}
}
@Data
public static class IrisSettingsPregen {
public boolean useCacheByDefault = true;
public boolean useHighPriority = false;
public boolean useVirtualThreads = false;
public boolean useTicketQueue = true;
public int maxConcurrency = 256;
}
@Data
public static class IrisSettingsPerformance {
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
public boolean trimMantleInStudio = false;
public int mantleKeepAlive = 30;
public int cacheSize = 4_096;
public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512;
public int tectonicPlateSize = -1;
public int mantleCleanupDelay = 200;
public int getTectonicPlateSize() {
if (tectonicPlateSize > 0)
return tectonicPlateSize;
return (int) (getHardware.getProcessMemory() / 200L);
}
}
@Data
@@ -180,6 +205,7 @@ public class IrisSettings {
public boolean DoomsdayAnnihilationSelfDestructMode = false;
public boolean commandSounds = true;
public boolean debug = false;
public boolean dumpMantleOnError = false;
public boolean disableNMS = false;
public boolean pluginMetrics = true;
public boolean splashLogoStartup = true;
@@ -199,6 +225,13 @@ public class IrisSettings {
}
}
@Data
public static class IrisSettingsSentry {
public boolean includeServerId = true;
public boolean disableAutoReporting = false;
public boolean debug = false;
}
@Data
public static class IrisSettingsGUI {
public boolean useServerLaunchedGuis = true;
@@ -220,4 +253,14 @@ public class IrisSettings {
public boolean disableTimeAndWeather = true;
public boolean autoStartDefaultStudio = false;
}
@Data
public static class IrisSettingsEngineSVC {
public boolean useVirtualThreads = true;
public int priority = Thread.NORM_PRIORITY;
public int getPriority() {
return Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
}
}
}

View File

@@ -0,0 +1,79 @@
package com.volmit.iris.core;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.io.IO;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.stream.Stream;
public class IrisWorlds {
private static final AtomicCache<IrisWorlds> cache = new AtomicCache<>();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Type TYPE = TypeToken.getParameterized(KMap.class, String.class, String.class).getType();
private final KMap<String, String> worlds;
private volatile boolean dirty = false;
private IrisWorlds(KMap<String, String> worlds) {
this.worlds = worlds;
save();
}
public static IrisWorlds get() {
return cache.aquire(() -> {
File file = Iris.instance.getDataFile("worlds.json");
if (!file.exists()) {
return new IrisWorlds(new KMap<>());
}
try {
String json = IO.readAll(file);
KMap<String, String> worlds = GSON.fromJson(json, TYPE);
return new IrisWorlds(Objects.requireNonNullElseGet(worlds, KMap::new));
} catch (Throwable e) {
Iris.error("Failed to load worlds.json!");
e.printStackTrace();
Iris.reportError(e);
}
return new IrisWorlds(new KMap<>());
});
}
public void put(String name, String type) {
String old = worlds.put(name, type);
if (!type.equals(old))
dirty = true;
save();
}
public Stream<File> getFolders() {
return worlds.keySet().stream().map(k -> new File(Bukkit.getWorldContainer(), k));
}
public void clean() {
dirty = worlds.entrySet().removeIf(entry -> !new File(Bukkit.getWorldContainer(), entry.getKey() + "/iris/pack/dimensions/" + entry.getValue() + ".json").exists());
}
public synchronized void save() {
clean();
if (!dirty) return;
try {
IO.write(Iris.instance.getDataFile("worlds.json"), OutputStreamWriter::new, writer -> GSON.toJson(worlds, TYPE, writer));
dirty = false;
} catch (IOException e) {
Iris.error("Failed to save worlds.json!");
e.printStackTrace();
Iris.reportError(e);
}
}
}

View File

@@ -23,10 +23,7 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisBiomeCustom;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisRange;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
@@ -34,8 +31,8 @@ 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.scheduling.J;
import lombok.Data;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
@@ -45,12 +42,13 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.stream.Stream;
import static com.volmit.iris.core.nms.datapack.IDataFixer.Dimension.*;
public class ServerConfigurator {
public static void configure() {
IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration();
@@ -71,10 +69,12 @@ public class ServerConfigurator {
f.load(spigotConfig);
long tt = f.getLong("settings.timeout-time");
if (tt < TimeUnit.MINUTES.toSeconds(5)) {
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)");
long spigotTimeout = TimeUnit.MINUTES.toSeconds(5);
if (tt < spigotTimeout) {
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + spigotTimeout + " (5 minutes)");
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
f.set("settings.timeout-time", TimeUnit.MINUTES.toSeconds(5));
f.set("settings.timeout-time", spigotTimeout);
f.save(spigotConfig);
}
}
@@ -84,10 +84,11 @@ public class ServerConfigurator {
f.load(spigotConfig);
long tt = f.getLong("watchdog.early-warning-delay");
if (tt < TimeUnit.MINUTES.toMillis(3)) {
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)");
long watchdog = TimeUnit.MINUTES.toMillis(3);
if (tt < watchdog) {
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + watchdog + " (3 minutes)");
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
f.set("watchdog.early-warning-delay", TimeUnit.MINUTES.toMillis(3));
f.set("watchdog.early-warning-delay", watchdog);
f.save(spigotConfig);
}
}
@@ -107,19 +108,25 @@ public class ServerConfigurator {
}
public static void installDataPacks(IDataFixer fixer, boolean fullInstall) {
if (fixer == null) {
Iris.error("Unable to install datapacks, fixer is null!");
return;
}
Iris.info("Checking Data Packs...");
DimensionHeight height = new DimensionHeight(fixer);
KList<File> folders = getDatapacksFolder();
KMap<String, KSet<String>> biomes = new KMap<>();
allPacks().flatMap(height::merge)
.parallel()
.forEach(dim -> {
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
});
try (Stream<IrisData> stream = allPacks()) {
stream.flatMap(height::merge)
.parallel()
.forEach(dim -> {
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
dim.installDimensionType(fixer, folders);
});
}
IrisDimension.writeShared(folders, height);
Iris.info("Data Packs Setup!");
if (fullInstall)
@@ -127,19 +134,22 @@ public class ServerConfigurator {
}
private static void verifyDataPacksPost(boolean allowRestarting) {
boolean bad = allPacks()
.map(data -> {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
var loader = data.getDimensionLoader();
return loader.loadAll(loader.getPossibleKeys())
.stream()
.map(ServerConfigurator::verifyDataPackInstalled)
.toList()
.contains(false);
})
.toList()
.contains(true);
if (!bad) return;
try (Stream<IrisData> stream = allPacks()) {
boolean bad = stream
.map(data -> {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
var loader = data.getDimensionLoader();
return loader.loadAll(loader.getPossibleKeys())
.stream()
.filter(Objects::nonNull)
.map(ServerConfigurator::verifyDataPackInstalled)
.toList()
.contains(false);
})
.toList()
.contains(true);
if (!bad) return;
}
if (allowRestarting) {
@@ -213,6 +223,11 @@ public class ServerConfigurator {
}
}
if (INMS.get().missingDimensionTypes(dimension.getDimensionTypeKey())) {
Iris.warn("The Dimension Type for " + dimension.getLoadFile() + " is not registered on the server.");
warn = true;
}
if (warn) {
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!");
@@ -222,9 +237,13 @@ public class ServerConfigurator {
}
public static Stream<IrisData> allPacks() {
return Stream.concat(listFiles(new File("plugins/Iris/packs")),
listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack")))
return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")),
IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack")))
.filter(File::isDirectory)
.filter( base -> {
var content = new File(base, "dimensions").listFiles();
return content != null && content.length > 0;
})
.map(IrisData::get);
}
@@ -239,49 +258,58 @@ public class ServerConfigurator {
return path.substring(worldContainer.length(), path.length() - l);
}
@SneakyThrows
private static Stream<File> listFiles(File parent) {
var files = parent.listFiles();
return files == null ? Stream.empty() : Arrays.stream(files);
if (!parent.isDirectory()) return Stream.empty();
return Files.walk(parent.toPath()).map(Path::toFile);
}
@Data
public static class DimensionHeight {
private final IDataFixer fixer;
private IrisRange overworld = new IrisRange();
private IrisRange nether = new IrisRange();
private IrisRange end = new IrisRange();
private int logicalOverworld = 0;
private int logicalNether = 0;
private int logicalEnd = 0;
private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];
public DimensionHeight(IDataFixer fixer) {
this.fixer = fixer;
for (int i = 0; i < 3; i++) {
dimensions[i] = new AtomicIntegerArray(new int[]{
Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE
});
}
}
public Stream<IrisDimension> merge(IrisData data) {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
var loader = data.getDimensionLoader();
return loader.loadAll(loader.getPossibleKeys())
.stream()
.filter(Objects::nonNull)
.peek(this::merge);
}
public void merge(IrisDimension dimension) {
overworld.merge(dimension.getDimensionHeight());
nether.merge(dimension.getDimensionHeight());
end.merge(dimension.getDimensionHeight());
logicalOverworld = Math.max(logicalOverworld, dimension.getLogicalHeight());
logicalNether = Math.max(logicalNether, dimension.getLogicalHeightNether());
logicalEnd = Math.max(logicalEnd, dimension.getLogicalHeightEnd());
AtomicIntegerArray array = dimensions[dimension.getBaseDimension().ordinal()];
array.updateAndGet(0, min -> Math.min(min, dimension.getMinHeight()));
array.updateAndGet(1, max -> Math.max(max, dimension.getMaxHeight()));
array.updateAndGet(2, logical -> Math.max(logical, dimension.getLogicalHeight()));
}
public String overworldType() {
return fixer.createDimension(OVERRWORLD, overworld, logicalOverworld).toString(4);
public String[] jsonStrings() {
var dims = IDataFixer.Dimension.values();
var arr = new String[3];
for (int i = 0; i < 3; i++) {
arr[i] = jsonString(dims[i]);
}
return arr;
}
public String netherType() {
return fixer.createDimension(NETHER, nether, logicalNether).toString(4);
}
public String endType() {
return fixer.createDimension(THE_END, end, logicalEnd).toString(4);
public String jsonString(IDataFixer.Dimension dimension) {
var data = dimensions[dimension.ordinal()];
int minY = data.get(0);
int maxY = data.get(1);
int logicalHeight = data.get(2);
if (minY == Integer.MAX_VALUE || maxY == Integer.MIN_VALUE || Integer.MIN_VALUE == logicalHeight)
return null;
return fixer.createDimension(dimension, minY, maxY - minY, logicalHeight, null).toString(4);
}
}
}

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.core.ServerConfigurator;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin;
import com.volmit.iris.util.decree.annotations.Decree;
@@ -36,6 +36,7 @@ import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
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"})
public class CommandDeveloper implements DecreeExecutor {
private CommandTurboPregen turboPregen;
private CommandLazyPregen lazyPregen;
private CommandUpdater updater;
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
public void EngineStatus() {
List<World> IrisWorlds = new ArrayList<>();
int TotalLoadedChunks = 0;
int TotalQueuedTectonicPlates = 0;
int TotalNotQueuedTectonicPlates = 0;
int TotalTectonicPlates = 0;
Iris.service(IrisEngineSVC.class)
.engineStatus(sender());
}
long lowestUnloadDuration = 0;
long highestUnloadDuration = 0;
@Decree(description = "Send a test exception to sentry")
public void Sentry() {
Engine engine = engine();
if (engine != null) IrisContext.getOr(engine);
Iris.reportError(new Exception("This is a test"));
}
for (World world : Bukkit.getWorlds()) {
try {
if (IrisToolbelt.access(world).getEngine() != null) {
IrisWorlds.add(world);
@Decree(description = "Test")
public void dumpThreads() {
try {
File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt");
FileOutputStream fos = new FileOutputStream(fi);
Map<Thread, StackTraceElement[]> f = Thread.getAllStackTraces();
PrintWriter pw = new PrintWriter(fos);
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());
}
} catch (Exception e) {
// no
}
}
for (World world : IrisWorlds) {
Engine engine = IrisToolbelt.access(world).getEngine();
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
for (Chunk chunk : world.getLoadedChunks()) {
if (chunk.isLoaded()) {
TotalLoadedChunks++;
}
pw.println("========================================");
pw.println();
pw.println();
}
pw.close();
Iris.info("DUMPED! See " + fi.getAbsolutePath());
} catch (Throwable e) {
e.printStackTrace();
}
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")
@@ -128,7 +128,7 @@ public class CommandDeveloper implements DecreeExecutor {
File tectonicplates = new File(folder, "mantle");
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
TectonicPlate.read(maxHeight, i);
TectonicPlate.read(maxHeight, i, true);
c++;
Iris.info("Loaded count: " + c );
@@ -234,7 +234,8 @@ public class CommandDeveloper implements DecreeExecutor {
@Param(description = "base IrisWorld") World world,
@Param(description = "raw TectonicPlate File") String path,
@Param(description = "Algorithm to Test") String algorithm,
@Param(description = "Amount of Tests") int amount) {
@Param(description = "Amount of Tests") int amount,
@Param(description = "Is versioned", defaultValue = "false") boolean versioned) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
return;
@@ -251,7 +252,7 @@ public class CommandDeveloper implements DecreeExecutor {
service.submit(() -> {
try {
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
TectonicPlate plate = new TectonicPlate(height, raw);
TectonicPlate plate = new TectonicPlate(height, raw, versioned);
raw.close();
double d1 = 0;
@@ -270,7 +271,7 @@ public class CommandDeveloper implements DecreeExecutor {
size = tmp.length();
start = System.currentTimeMillis();
CountingDataInputStream din = createInput(tmp, algorithm);
new TectonicPlate(height, din);
new TectonicPlate(height, din, true);
din.close();
d2 += System.currentTimeMillis() - start;
tmp.delete();

View File

@@ -19,17 +19,13 @@
package com.volmit.iris.core.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.object.*;
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.decree.specialhandlers.NullableBiomeHandler;
import com.volmit.iris.util.decree.specialhandlers.NullableRegionHandler;
import com.volmit.iris.util.format.C;
import org.bukkit.block.Biome;
import java.awt.*;
@@ -55,31 +51,12 @@ public class CommandEdit implements DecreeExecutor {
@Decree(description = "Edit the biome you specified", aliases = {"b"}, origin = DecreeOrigin.PLAYER)
public void biome(@Param(contextual = false, description = "The biome to edit", defaultValue = "---", customHandler = NullableBiomeHandler.class) IrisBiome biome) {
public void biome(@Param(contextual = false, description = "The biome to edit") IrisBiome biome) {
if (noStudio()) {
return;
}
if (biome == null) {
try {
IrisBiome b = engine().getBiome(player().getLocation().getBlockX(), player().getLocation().getBlockY() - player().getWorld().getMinHeight(), player().getLocation().getBlockZ());
Desktop.getDesktop().open(b.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + b.getTypeName() + " " + b.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage("Non-Iris Biome: " + player().getLocation().getBlock().getBiome().name());
if (player().getLocation().getBlock().getBiome().equals(Biome.CUSTOM)) {
try {
sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")");
} catch (Throwable ee) {
Iris.reportError(ee);
}
}
}
return;
}
try {
if (biome.getLoadFile() == null) {
if (biome == null || biome.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
return;
}
@@ -92,20 +69,10 @@ public class CommandEdit implements DecreeExecutor {
}
@Decree(description = "Edit the region you specified", aliases = {"r"}, origin = DecreeOrigin.PLAYER)
public void region(@Param(contextual = false, description = "The region to edit", defaultValue = "---", customHandler = NullableRegionHandler.class) IrisRegion region) {
public void region(@Param(contextual = false, description = "The region to edit") IrisRegion region) {
if (noStudio()) {
return;
}
if (region == null) {
try {
IrisRegion r = engine().getRegion(player().getLocation().getBlockX(), player().getLocation().getBlockZ());
Desktop.getDesktop().open(r.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + r.getTypeName() + " " + r.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
} catch (Throwable e) {
sender().sendMessage(C.RED + "Failed to get region.");
}
return;
}
try {
if (region == null || region.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");

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.pregenerator.ChunkUpdater;
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.engine.framework.Engine;
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.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.ServerBootSFG.incompatibilities;
import static org.bukkit.Bukkit.getServer;
@@ -68,14 +66,12 @@ import static org.bukkit.Bukkit.getServer;
public class CommandIris implements DecreeExecutor {
private CommandStudio studio;
private CommandPregen pregen;
private CommandLazyPregen lazyPregen;
private CommandSettings settings;
private CommandObject object;
private CommandJigsaw jigsaw;
private CommandWhat what;
private CommandEdit edit;
private CommandFind find;
private CommandSupport support;
private CommandDeveloper developer;
public static boolean worldCreation = false;
String WorldEngine;
@@ -175,16 +171,6 @@ public class CommandIris implements DecreeExecutor {
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
@Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE)

View File

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

View File

@@ -39,7 +39,9 @@ public class CommandPregen implements DecreeExecutor {
@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
Vector center,
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
boolean gui
) {
try {
if (sender().isPlayer() && access() == null) {
@@ -50,7 +52,7 @@ public class CommandPregen implements DecreeExecutor {
IrisToolbelt.pregenerate(PregenTask
.builder()
.center(new Position2(center.getBlockX(), center.getBlockZ()))
.gui(true)
.gui(gui)
.radiusX(radius)
.radiusZ(radius)
.build(), world);

View File

@@ -22,7 +22,6 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.gui.NoiseExplorerGUI;
import com.volmit.iris.core.gui.VisionGUI;
import com.volmit.iris.core.gui.components.IrisRenderer;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.project.IrisProject;
import com.volmit.iris.core.service.ConversionSVC;
@@ -66,9 +65,6 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -160,37 +156,6 @@ public class CommandStudio implements DecreeExecutor {
sender().sendMessage(C.GREEN + "The \"" + dimension.getName() + "\" pack has version: " + dimension.getVersion());
}
@Decree(description = "Debug image")
public void printImageChannel(
@Param(name = "image", description = "Image found in the image folder inside the project")
String image,
@Param(name = "channel", description = "Image channel")
IrisImageChannel channel
) {
if (noStudio()) return;
try {
File file = new File(access().getEngine().getComplex().getData().getDataFolder(), "images/" + image);
if (!file.exists()) {
sender().sendMessage(C.RED + "The image \"" + image + "\" does not exist.");
return;
}
BufferedImage buffer = ImageIO.read(file);
IrisImage irisImage = new IrisImage(buffer);
File at = new File(file.getParentFile(), "debug-see-" + file.getName());
BufferedImage b = new BufferedImage(buffer.getWidth(), buffer.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < buffer.getWidth(); i++) {
for (int j = 0; j < buffer.getHeight(); j++) {
b.setRGB(i, j, Color.getHSBColor(0, 0, (float) irisImage.getValue(channel, i, j)).getRGB());
}
}
ImageIO.write(b, "png", at);
sender().sendMessage(C.IRIS + "Debug image written to ./images for channel " + channel.name());
} catch (Exception e) {
sender().sendMessage(C.RED + "Something went wrong.. ");
e.printStackTrace();
}
}
@Decree(name = "regen", description = "Regenerate nearby chunks.", aliases = "rg", sync = true, origin = DecreeOrigin.PLAYER)
public void regen(
@Param(name = "radius", description = "The radius of nearby cunks", defaultValue = "5")
@@ -439,16 +404,9 @@ public class CommandStudio implements DecreeExecutor {
@Param(name = "world", description = "The world to open the generator for", contextual = true)
World world
) {
if (noGUI()) {
sender().sendMessage(C.GOLD + "GUI Support isn't enabled or supported on this server.");
return;
}
if (noGUI()) return;
if (world == null) {
if (noStudio()) return;
world = player().getWorld();
}
else if (!IrisToolbelt.isIrisWorld(world)) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "You need to be in or specify an Iris-generated world!");
return;
}
@@ -884,7 +842,7 @@ public class CommandStudio implements DecreeExecutor {
sender().sendMessage(C.RED + "No studio world is open!");
return true;
}
if (engine() == null || !engine().isStudio()) {
if (!engine().isStudio()) {
sender().sendMessage(C.RED + "You must be in a studio world!");
return true;
}

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

@@ -40,7 +40,10 @@ import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -53,7 +56,7 @@ public class PregeneratorJob implements PregenListener {
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
private static final Color COLOR_GENERATED = parseColor("#65c295");
private static final Color COLOR_CLEANED = parseColor("#34eb93");
public static PregeneratorJob instance;
private static final AtomicReference<PregeneratorJob> instance = new AtomicReference<>();
private final MemoryMonitor monitor;
private final PregenTask task;
private final boolean saving;
@@ -64,14 +67,21 @@ public class PregeneratorJob implements PregenListener {
private final Position2 max;
private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1));
private final Engine engine;
private final ExecutorService service;
private JFrame frame;
private PregenRenderer renderer;
private int rgc = 0;
private String[] info;
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
instance.updateAndGet(old -> {
if (old != null) {
old.pregenerator.close();
old.close();
}
return this;
});
this.engine = engine;
instance = this;
monitor = new MemoryMonitor(50);
saving = false;
info = new String[]{"Initializing..."};
@@ -96,40 +106,44 @@ public class PregeneratorJob implements PregenListener {
}, "Iris Pregenerator");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
service = Executors.newVirtualThreadPerTaskExecutor();
}
public static boolean shutdownInstance() {
if (instance == null) {
PregeneratorJob inst = instance.get();
if (inst == null) {
return false;
}
J.a(() -> instance.pregenerator.close());
J.a(inst.pregenerator::close);
return true;
}
public static PregeneratorJob getInstance() {
return instance;
return instance.get();
}
public static boolean pauseResume() {
if (instance == null) {
PregeneratorJob inst = instance.get();
if (inst == null) {
return false;
}
if (isPaused()) {
instance.pregenerator.resume();
inst.pregenerator.resume();
} else {
instance.pregenerator.pause();
inst.pregenerator.pause();
}
return true;
}
public static boolean isPaused() {
if (instance == null) {
PregeneratorJob inst = instance.get();
if (inst == null) {
return true;
}
return instance.paused();
return inst.paused();
}
private static Color parseColor(String c) {
@@ -179,7 +193,7 @@ public class PregeneratorJob implements PregenListener {
J.a(() -> {
pregenerator.close();
close();
instance = null;
instance.compareAndSet(this, null);
});
}
@@ -219,10 +233,10 @@ public class PregeneratorJob implements PregenListener {
}
@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[]{
(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)",
"Generation Method: " + method,
"Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s",
@@ -240,13 +254,16 @@ public class PregeneratorJob implements PregenListener {
}
@Override
public void onChunkGenerated(int x, int z) {
if (engine != null) {
draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
return;
}
public void onChunkGenerated(int x, int z, boolean cached) {
if (renderer == null || frame == null || !frame.isVisible()) return;
service.submit(() -> {
if (engine != null) {
draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
return;
}
draw(x, z, COLOR_GENERATED);
draw(x, z, COLOR_GENERATED);
});
}
@Override
@@ -304,8 +321,9 @@ public class PregeneratorJob implements PregenListener {
@Override
public void onClose() {
close();
instance = null;
instance.compareAndSet(this, null);
whenDone.forEach(Runnable::run);
service.shutdownNow();
}
@Override

View File

@@ -1,6 +1,6 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2024 Arcane Arts (Volmit Software)
* 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
@@ -30,6 +30,7 @@ import com.volmit.iris.engine.object.IrisWorld;
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.data.registry.Attributes;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.M;
@@ -56,9 +57,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BiFunction;
//todo
// - Misalignment
// - Weird snapping?
import static com.volmit.iris.util.data.registry.Attributes.MAX_HEALTH;
public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener, MouseMotionListener, MouseInputListener {
private static final long serialVersionUID = 2094606939770332040L;
private final KList<LivingEntity> lastEntities = new KList<>();
@@ -415,8 +415,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
}
private double getWorldX(double screenX) {
return (mscale * screenX) + ((oxp / scale) * mscale);
// return (mscale * screenX) + ((oxp / scale));
//return (mscale * screenX) + ((oxp / scale) * mscale);
return (mscale * screenX) + ((oxp / scale));
}
private double getWorldZ(double screenZ) {
@@ -639,7 +639,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
k.add("Pos: " + h.getLocation().getBlockX() + ", " + h.getLocation().getBlockY() + ", " + h.getLocation().getBlockZ());
k.add("UUID: " + h.getUniqueId());
k.add("HP: " + h.getHealth() + " / " + h.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
k.add("HP: " + h.getHealth() + " / " + h.getAttribute(MAX_HEALTH).getValue());
drawCardTR(g, k);
}

View File

@@ -0,0 +1,88 @@
package com.volmit.iris.core.link;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData;
import me.kryniowesegryderiusz.kgenerators.Main;
import me.kryniowesegryderiusz.kgenerators.api.KGeneratorsAPI;
import me.kryniowesegryderiusz.kgenerators.generators.locations.objects.GeneratorLocation;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.MissingResourceException;
public class KGeneratorsDataProvider extends ExternalDataProvider {
public KGeneratorsDataProvider() {
super("KGenerators");
}
@Override
public void init() {
}
@Override
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) throws MissingResourceException {
if (Main.getGenerators().get(blockId.key()) == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
return new IrisCustomData(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
}
@Override
public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) throws MissingResourceException {
var gen = Main.getGenerators().get(itemId.key());
if (gen == null) throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key());
return gen.getGeneratorItem();
}
@Override
public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) {
if (block.getType() != Material.STRUCTURE_VOID) return;
var existing = KGeneratorsAPI.getLoadedGeneratorLocation(block.getLocation());
if (existing != null) return;
block.setBlockData(B.getAir(), false);
var gen = Main.getGenerators().get(blockId.key());
if (gen == null) return;
var loc = new GeneratorLocation(-1, gen, block.getLocation(), Main.getPlacedGenerators().getChunkInfo(block.getChunk()), null, null);
Main.getDatabases().getDb().saveGenerator(loc);
Main.getPlacedGenerators().addLoaded(loc);
Main.getSchedules().schedule(loc, true);
}
@Override
public @NotNull Identifier[] getBlockTypes() {
return Main.getGenerators().getAll().stream()
.map(gen -> new Identifier("kgenerators", gen.getId()))
.filter(i -> {
try {
return getBlockData(i) != null;
} catch (MissingResourceException e) {
return false;
}
})
.toArray(Identifier[]::new);
}
@Override
public @NotNull Identifier[] getItemTypes() {
return Main.getGenerators().getAll().stream()
.map(gen -> new Identifier("kgenerators", gen.getId()))
.filter(i -> {
try {
return getItemStack(i) != null;
} catch (MissingResourceException e) {
return false;
}
})
.toArray(Identifier[]::new);
}
@Override
public boolean isValidProvider(@NotNull Identifier id, boolean isItem) {
return "kgenerators".equalsIgnoreCase(id.namespace());
}
}

View File

@@ -18,126 +18,60 @@
package com.volmit.iris.core.link;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.collection.KMap;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import org.mvplugins.multiverse.core.MultiverseCoreApi;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
public class MultiverseCoreLink {
private final KMap<String, String> worldNameTypes = new KMap<>();
private final boolean active;
public MultiverseCoreLink() {
}
public boolean addWorld(String worldName, IrisDimension dim, String seed) {
if (!isSupported()) {
return false;
}
try {
Plugin p = getMultiverse();
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
Method m = mvWorldManager.getClass().getDeclaredMethod("addWorld",
String.class, World.Environment.class, String.class, WorldType.class, Boolean.class, String.class, boolean.class);
boolean b = (boolean) m.invoke(mvWorldManager, worldName, dim.getEnvironment(), seed, WorldType.NORMAL, false, "Iris", false);
saveConfig();
return b;
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
return false;
}
@SuppressWarnings("unchecked")
public Map<String, ?> getList() {
try {
Plugin p = getMultiverse();
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
Field f = mvWorldManager.getClass().getDeclaredField("worldsFromTheConfig");
f.setAccessible(true);
return (Map<String, ?>) f.get(mvWorldManager);
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
return null;
active = Bukkit.getPluginManager().getPlugin("Multiverse-Core") != null;
}
public void removeFromConfig(World world) {
if (!isSupported()) {
return;
}
getList().remove(world.getName());
saveConfig();
removeFromConfig(world.getName());
}
public void removeFromConfig(String world) {
if (!isSupported()) {
return;
if (!active) return;
var manager = worldManager();
manager.removeWorld(world).onSuccess(manager::saveWorldsConfig);
}
@SneakyThrows
public void updateWorld(World bukkitWorld, String pack) {
if (!active) return;
var generator = "Iris:" + pack;
var manager = worldManager();
var world = manager.getWorld(bukkitWorld).getOrElse(() -> {
var options = ImportWorldOptions.worldName(bukkitWorld.getName())
.generator(generator)
.environment(bukkitWorld.getEnvironment())
.useSpawnAdjust(false);
return manager.importWorld(options).get();
});
world.setAutoLoad(false);
if (!generator.equals(world.getGenerator())) {
var field = MultiverseWorld.class.getDeclaredField("worldConfig");
field.setAccessible(true);
var config = field.get(world);
config.getClass()
.getDeclaredMethod("setGenerator", String.class)
.invoke(config, generator);
}
getList().remove(world);
saveConfig();
manager.saveWorldsConfig();
}
public void saveConfig() {
try {
Plugin p = getMultiverse();
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
mvWorldManager.getClass().getDeclaredMethod("saveWorldsConfig").invoke(mvWorldManager);
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
}
public void assignWorldType(String worldName, String type) {
worldNameTypes.put(worldName, type);
}
public String getWorldNameType(String worldName, String defaultType) {
try {
String t = worldNameTypes.get(worldName);
return t == null ? defaultType : t;
} catch (Throwable e) {
Iris.reportError(e);
return defaultType;
}
}
public boolean isSupported() {
return getMultiverse() != null;
}
public Plugin getMultiverse() {
return Bukkit.getPluginManager().getPlugin("Multiverse-Core");
}
public String envName(World.Environment environment) {
if (environment == null) {
return "normal";
}
return switch (environment) {
case NORMAL -> "normal";
case NETHER -> "nether";
case THE_END -> "end";
default -> environment.toString().toLowerCase();
};
private WorldManager worldManager() {
var api = MultiverseCoreApi.get();
return api.getWorldManager();
}
}

View File

@@ -18,20 +18,33 @@
package com.volmit.iris.core.link;
import com.google.common.collect.Sets;
import com.volmit.iris.Iris;
import com.volmit.iris.core.tools.IrisToolbelt;
import io.lumine.mythic.api.adapters.AbstractLocation;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.api.skills.conditions.ILocationCondition;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.bukkit.adapters.BukkitWorld;
import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent;
import io.lumine.mythic.core.skills.SkillCondition;
import io.lumine.mythic.core.utils.annotations.MythicCondition;
import io.lumine.mythic.core.utils.annotations.MythicField;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.*;
public class MythicMobsLink {
public MythicMobsLink() {
if (getPlugin() == null) return;
Iris.instance.registerListener(new ConditionListener());
}
public boolean isEnabled() {
@@ -49,12 +62,70 @@ public class MythicMobsLink {
* @param location The location
* @return The mob, or null if it can't be spawned
*/
public @Nullable
Entity spawnMob(String mob, Location location) {
public @Nullable Entity spawnMob(String mob, Location location) {
return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null;
}
public Collection<String> getMythicMobTypes() {
return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of();
}
private static class ConditionListener implements Listener {
@EventHandler
public void on(MythicConditionLoadEvent event) {
switch (event.getConditionName()) {
case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig()));
case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig()));
}
}
}
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
public static class IrisBiomeCondition extends SkillCondition implements ILocationCondition {
@MythicField(name = "biome", aliases = {"b"}, description = "A list of biomes to check")
private Set<String> biomes = Sets.newConcurrentHashSet();
@MythicField(name = "surface", aliases = {"s"}, description = "If the biome check should only be performed on the surface")
private boolean surface;
public IrisBiomeCondition(String line, MythicLineConfig mlc) {
super(line);
String b = mlc.getString(new String[]{"biome", "b"}, "");
biomes.addAll(Arrays.asList(b.split(",")));
surface = mlc.getBoolean(new String[]{"surface", "s"}, false);
}
@Override
public boolean check(AbstractLocation target) {
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
if (access == null) return false;
var engine = access.getEngine();
if (engine == null) return false;
var biome = surface ?
engine.getSurfaceBiome(target.getBlockX(), target.getBlockZ()) :
engine.getBiomeOrMantle(target.getBlockX(), target.getBlockY() - engine.getMinHeight(), target.getBlockZ());
return biomes.contains(biome.getLoadKey());
}
}
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
public static class IrisRegionCondition extends SkillCondition implements ILocationCondition {
@MythicField(name = "region", aliases = {"r"}, description = "A list of regions to check")
private Set<String> regions = Sets.newConcurrentHashSet();
public IrisRegionCondition(String line, MythicLineConfig mlc) {
super(line);
String b = mlc.getString(new String[]{"region", "r"}, "");
regions.addAll(Arrays.asList(b.split(",")));
}
@Override
public boolean check(AbstractLocation target) {
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
if (access == null) return false;
var engine = access.getEngine();
if (engine == null) return false;
var region = engine.getRegion(target.getBlockX(), target.getBlockZ());
return regions.contains(region.getLoadKey());
}
}
}

View File

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

View File

@@ -44,6 +44,8 @@ import lombok.Data;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Function;
@@ -300,6 +302,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return r;
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
Iris.error("Failed to create loader! " + registrant.getCanonicalName());
}
@@ -360,6 +363,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
for (ResourceLoader<?> i : loaders.values()) {
i.clearList();
}
possibleSnippets.clear();
}
public String toLoadKey(File f) {
@@ -426,8 +430,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
File f = new File(getDataFolder(), r + ".json");
if (f.exists()) {
try {
JsonReader snippetReader = new JsonReader(new FileReader(f));
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
return adapter.read(snippetReader);
} catch (Throwable e) {
Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")");
@@ -461,11 +464,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
KList<String> l = new KList<>();
File snippetFolder = new File(getDataFolder(), "snippet/" + f);
if (!snippetFolder.exists()) return l;
if (snippetFolder.exists() && snippetFolder.isDirectory()) {
for (File i : snippetFolder.listFiles()) {
l.add("snippet/" + f + "/" + i.getName().split("\\Q.\\E")[0]);
}
String absPath = snippetFolder.getAbsolutePath();
try (var stream = Files.walk(snippetFolder.toPath())) {
stream.filter(Files::isRegularFile)
.map(Path::toAbsolutePath)
.map(Path::toString)
.filter(s -> s.endsWith(".json"))
.map(s -> s.substring(absPath.length() + 1))
.map(s -> s.replace("\\", "/"))
.map(s -> s.split("\\Q.\\E")[0])
.forEach(s -> l.add("snippet/" + f + "/" + s));
} catch (Throwable e) {
e.printStackTrace();
}
return l;

View File

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

View File

@@ -34,9 +34,13 @@ public class INMS {
"1.21.1", "v1_21_R1",
"1.21.2", "v1_21_R2",
"1.21.3", "v1_21_R2",
"1.21.4", "v1_21_R3"
"1.21.4", "v1_21_R3",
"1.21.5", "v1_21_R4",
"1.21.6", "v1_21_R5",
"1.21.7", "v1_21_R5"
);
private static final List<Version> PACKS = List.of(
new Version(21, 5, "31100"),
new Version(21, 4, "31020"),
new Version(21, 2, "31000"),
new Version(20, 1, "3910")

View File

@@ -18,11 +18,10 @@
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.Pair;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.mantle.Mantle;
@@ -37,6 +36,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color;
@@ -90,12 +90,10 @@ public interface INMSBinding {
MCABiomeContainer newBiomeContainer(int min, int max);
default World createWorld(WorldCreator c) {
if (missingDimensionTypes(true, true, true))
throw new IllegalStateException("Missing dimenstion types to create world");
try (var ignored = injectLevelStems()) {
return c.createWorld();
}
if (c.generator() instanceof PlatformChunkGenerator gen
&& missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey()))
throw new IllegalStateException("Missing dimension types to create world");
return c.createWorld();
}
int countCustomBiomes();
@@ -130,9 +128,9 @@ public interface INMSBinding {
KList<String> getStructureKeys();
AutoClosing injectLevelStems();
boolean missingDimensionTypes(String... keys);
Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end);
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
default boolean injectBukkit() {
return true;
}
}

View File

@@ -1,5 +1,6 @@
package com.volmit.iris.core.nms.container;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.function.NastyRunnable;
import lombok.AllArgsConstructor;
@@ -7,6 +8,7 @@ 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;
@@ -14,9 +16,24 @@ public class AutoClosing implements AutoCloseable {
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

@@ -13,6 +13,7 @@ import java.util.function.Supplier;
//https://minecraft.wiki/w/Pack_format
@Getter
public enum DataVersion {
UNSUPPORTED("0.0.0", 0, () -> null),
V1192("1.19.2", 10, DataFixerV1192::new),
V1205("1.20.6", 41, DataFixerV1206::new),
V1213("1.21.3", 57, DataFixerV1213::new);

View File

@@ -1,28 +1,31 @@
package com.volmit.iris.core.nms.datapack;
import com.volmit.iris.engine.object.IrisBiomeCustom;
import com.volmit.iris.engine.object.IrisRange;
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
import com.volmit.iris.util.json.JSONObject;
import org.jetbrains.annotations.Nullable;
public interface IDataFixer {
default JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
return json;
}
JSONObject rawDimension(Dimension dimension);
JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options);
default JSONObject createDimension(Dimension dimension, IrisRange height, int logicalHeight) {
JSONObject obj = rawDimension(dimension);
obj.put("min_y", height.getMin());
obj.put("height", height.getMax() - height.getMin());
void fixDimension(Dimension dimension, JSONObject json);
default JSONObject createDimension(Dimension base, int minY, int height, int logicalHeight, @Nullable IrisDimensionTypeOptions options) {
JSONObject obj = resolve(base, options);
obj.put("min_y", minY);
obj.put("height", height);
obj.put("logical_height", logicalHeight);
fixDimension(base, obj);
return obj;
}
enum Dimension {
OVERRWORLD,
OVERWORLD,
NETHER,
THE_END
END
}
}

View File

@@ -1,81 +1,104 @@
package com.volmit.iris.core.nms.datapack.v1192;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
import com.volmit.iris.util.json.JSONObject;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
public class DataFixerV1192 implements IDataFixer {
private static final Map<Dimension, IrisDimensionTypeOptions> OPTIONS = Map.of(
Dimension.OVERWORLD, new IrisDimensionTypeOptions(
FALSE,
TRUE,
FALSE,
FALSE,
TRUE,
TRUE,
TRUE,
FALSE,
1d,
0f,
null,
192,
0),
Dimension.NETHER, new IrisDimensionTypeOptions(
TRUE,
FALSE,
TRUE,
TRUE,
FALSE,
FALSE,
FALSE,
TRUE,
8d,
0.1f,
18000L,
null,
15),
Dimension.END, new IrisDimensionTypeOptions(
FALSE,
FALSE,
FALSE,
FALSE,
FALSE,
TRUE,
FALSE,
FALSE,
1d,
0f,
6000L,
null,
0)
);
private static final Map<Dimension, String> DIMENSIONS = Map.of(
Dimension.OVERRWORLD, """
Dimension.OVERWORLD, """
{
"ambient_light": 0.0,
"bed_works": true,
"coordinate_scale": 1.0,
"effects": "minecraft:overworld",
"has_ceiling": false,
"has_raids": true,
"has_skylight": true,
"infiniburn": "#minecraft:infiniburn_overworld",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": {
"type": "minecraft:uniform",
"value": {
"max_inclusive": 7,
"min_inclusive": 0
}
},
"natural": true,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}
}""",
Dimension.NETHER, """
{
"ambient_light": 0.1,
"bed_works": false,
"coordinate_scale": 8.0,
"effects": "minecraft:the_nether",
"fixed_time": 18000,
"has_ceiling": true,
"has_raids": false,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_nether",
"monster_spawn_block_light_limit": 15,
"monster_spawn_light_level": 7,
"natural": false,
"piglin_safe": true,
"respawn_anchor_works": true,
"ultrawarm": true
}""",
Dimension.THE_END, """
Dimension.END, """
{
"ambient_light": 0.0,
"bed_works": false,
"coordinate_scale": 1.0,
"effects": "minecraft:the_end",
"fixed_time": 6000,
"has_ceiling": false,
"has_raids": true,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_end",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": {
"type": "minecraft:uniform",
"value": {
"max_inclusive": 7,
"min_inclusive": 0
}
},
"natural": false,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}
}"""
);
@Override
public JSONObject rawDimension(Dimension dimension) {
return new JSONObject(DIMENSIONS.get(dimension));
public JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options) {
return options == null ? OPTIONS.get(dimension).toJson() : options.resolve(OPTIONS.get(dimension)).toJson();
}
@Override
public void fixDimension(Dimension dimension, JSONObject json) {
var missing = new JSONObject(DIMENSIONS.get(dimension));
for (String key : missing.keySet()) {
if (json.has(key)) continue;
json.put(key, missing.get(key));
}
}
}

View File

@@ -45,13 +45,12 @@ public class DataFixerV1206 extends DataFixerV1192 {
}
@Override
public JSONObject rawDimension(Dimension dimension) {
JSONObject json = super.rawDimension(dimension);
public void fixDimension(Dimension dimension, JSONObject json) {
super.fixDimension(dimension, json);
if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel))
return json;
return;
var value = (JSONObject) lightLevel.remove("value");
lightLevel.put("max_inclusive", value.get("max_inclusive"));
lightLevel.put("min_inclusive", value.get("min_inclusive"));
return json;
}
}

View File

@@ -20,9 +20,8 @@ package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
@@ -121,17 +120,7 @@ public class NMSBinding1X implements INMSBinding {
}
@Override
public AutoClosing injectLevelStems() {
return new AutoClosing(() -> {});
}
@Override
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
return new Pair<>(0, new AutoClosing(() -> {}));
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
public boolean missingDimensionTypes(String... keys) {
return false;
}
@@ -219,6 +208,11 @@ public class NMSBinding1X implements INMSBinding {
return true;
}
@Override
public DataVersion getDataVersion() {
return DataVersion.UNSUPPORTED;
}
@Override
public int getBiomeId(Biome biome) {
return biome.ordinal();

View File

@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class ChunkUpdater {
private static final String REGION_PATH = "region" + File.separator + "r.";
private final AtomicBoolean paused = new AtomicBoolean();
private final AtomicBoolean cancelled = new AtomicBoolean();
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
@@ -108,6 +109,7 @@ public class ChunkUpdater {
}
}
} catch (Exception e) {
Iris.reportError(e);
e.printStackTrace();
}
}, 0, 3, TimeUnit.SECONDS);
@@ -162,12 +164,12 @@ public class ChunkUpdater {
J.sleep(50);
}
if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) {
return;
}
if (!new File(world.getWorldFolder(), "region" + File.separator + rX + "." + rZ + ".mca").exists()) {
return;
}
if (rX < dimensions.min.getX() ||
rX > dimensions.max.getX() ||
rZ < dimensions.min.getZ() ||
rZ > dimensions.max.getZ() ||
!new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists()
) return;
task.iterateChunks(rX, rZ, (x, z) -> {
while (paused.get() && !cancelled.get()) {
@@ -313,6 +315,7 @@ public class ChunkUpdater {
world.save();
}).get();
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
}

View File

@@ -31,28 +31,33 @@ import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
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.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
public class IrisPregenerator {
private static final double INVALID = 9223372036854775807d;
private final PregenTask task;
private final PregeneratorMethod generator;
private final PregenListener listener;
private final Looper ticker;
private final AtomicBoolean paused;
private final AtomicBoolean shutdown;
private final RollingSequence cachedPerSecond;
private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute;
private final RollingSequence regionsPerMinute;
private final KList<Integer> chunksPerSecondHistory;
private static AtomicInteger generated;
private final AtomicInteger generatedLast;
private final AtomicInteger generatedLastMinute;
private static AtomicInteger totalChunks;
private final AtomicLong generated;
private final AtomicLong generatedLast;
private final AtomicLong generatedLastMinute;
private final AtomicLong cached;
private final AtomicLong cachedLast;
private final AtomicLong cachedLastMinute;
private final AtomicLong totalChunks;
private final AtomicLong startTime;
private final ChronoLatch minuteLatch;
private final AtomicReference<String> currentGeneratorMethod;
@@ -61,8 +66,10 @@ public class IrisPregenerator {
private final KSet<Position2> net;
private final ChronoLatch cl;
private final ChronoLatch saveLatch = new ChronoLatch(30000);
private final IrisPackBenchmarking benchmarking;
public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) {
benchmarking = IrisPackBenchmarking.getInstance();
this.listener = listenify(listener);
cl = new ChronoLatch(5000);
generatedRegions = new KSet<>();
@@ -74,46 +81,71 @@ public class IrisPregenerator {
net = new KSet<>();
currentGeneratorMethod = new AtomicReference<>("Void");
minuteLatch = new ChronoLatch(60000, false);
cachedPerSecond = new RollingSequence(5);
chunksPerSecond = new RollingSequence(10);
chunksPerMinute = new RollingSequence(10);
regionsPerMinute = new RollingSequence(10);
chunksPerSecondHistory = new KList<>();
generated = new AtomicInteger(0);
generatedLast = new AtomicInteger(0);
generatedLastMinute = new AtomicInteger(0);
totalChunks = new AtomicInteger(0);
generated = new AtomicLong(0);
generatedLast = new AtomicLong(0);
generatedLastMinute = new AtomicLong(0);
cached = new AtomicLong();
cachedLast = new AtomicLong(0);
cachedLastMinute = new AtomicLong(0);
totalChunks = new AtomicLong(0);
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
startTime = new AtomicLong(M.ms());
ticker = new Looper() {
@Override
protected long loop() {
long eta = computeETA();
int secondGenerated = generated.get() - generatedLast.get();
generatedLast.set(generated.get());
chunksPerSecond.put(secondGenerated);
chunksPerSecondHistory.add(secondGenerated);
if (minuteLatch.flip()) {
int minuteGenerated = generated.get() - generatedLastMinute.get();
generatedLastMinute.set(generated.get());
chunksPerMinute.put(minuteGenerated);
regionsPerMinute.put((double) minuteGenerated / 1024D);
long secondCached = cached.get() - cachedLast.get();
cachedLast.set(cached.get());
cachedPerSecond.put(secondCached);
long secondGenerated = generated.get() - generatedLast.get() - secondCached;
generatedLast.set(generated.get());
if (secondCached == 0 || secondGenerated != 0) {
chunksPerSecond.put(secondGenerated);
chunksPerSecondHistory.add((int) secondGenerated);
}
listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(),
if (minuteLatch.flip()) {
long minuteCached = cached.get() - cachedLastMinute.get();
cachedLastMinute.set(cached.get());
long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached;
generatedLastMinute.set(generated.get());
if (minuteCached == 0 || minuteGenerated != 0) {
chunksPerMinute.put(minuteGenerated);
regionsPerMinute.put((double) minuteGenerated / 1024D);
}
}
boolean cached = cachedPerSecond.getAverage() != 0;
listener.onTick(
cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(),
chunksPerMinute.getAverage(),
regionsPerMinute.getAverage(),
(double) generated.get() / (double) totalChunks.get(),
generated.get(), totalChunks.get(),
totalChunks.get() - generated.get(),
eta, M.ms() - startTime.get(), currentGeneratorMethod.get());
(double) generated.get() / (double) totalChunks.get(), generated.get(),
totalChunks.get(),
totalChunks.get() - generated.get(), eta, M.ms() - startTime.get(), currentGeneratorMethod.get(),
cached);
if (cl.flip()) {
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);
} else {
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);
}
Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
benchmarking != null ? "Benchmarking" : "Pregen",
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;
}
@@ -121,12 +153,12 @@ public class IrisPregenerator {
}
private long computeETA() {
return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
double d = (long) (generated.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)
((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) :
// 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()) / chunksPerSecond.getAverage()) * 1000);
return Double.isFinite(d) && d != INVALID ? (long) d : 0;
}
@@ -138,13 +170,15 @@ public class IrisPregenerator {
init();
ticker.start();
checkRegions();
var p = PrecisionStopwatch.start();
task.iterateRegions((x, z) -> visitRegion(x, z, true));
task.iterateRegions((x, z) -> visitRegion(x, z, false));
Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
shutdown();
if (!IrisPackBenchmarking.benchmarkInProgress) {
if (benchmarking == null) {
Iris.info(C.IRIS + "Pregen stopped.");
} else {
IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory);
benchmarking.finishedBenchmark(chunksPerSecondHistory);
}
}
@@ -234,8 +268,8 @@ public class IrisPregenerator {
private PregenListener listenify(PregenListener listener) {
return new PregenListener() {
@Override
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, 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, cached);
}
@Override
@@ -244,9 +278,10 @@ public class IrisPregenerator {
}
@Override
public void onChunkGenerated(int x, int z) {
listener.onChunkGenerated(x, z);
public void onChunkGenerated(int x, int z, boolean c) {
listener.onChunkGenerated(x, z, c);
generated.addAndGet(1);
if (c) cached.addAndGet(1);
}
@Override

View File

@@ -19,11 +19,15 @@
package com.volmit.iris.core.pregenerator;
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 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);

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

@@ -32,26 +32,32 @@ import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.util.ArrayList;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class AsyncPregenMethod implements PregeneratorMethod {
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private final World world;
private final MultiBurst burst;
private final Executor executor;
private final Semaphore semaphore;
private final int threads;
private final boolean urgent;
private final Map<Chunk, Long> lastUse;
public AsyncPregenMethod(World world, int threads) {
public AsyncPregenMethod(World world, int unusedThreads) {
if (!PaperLib.isPaper()) {
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
}
this.world = world;
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
semaphore = new Semaphore(256);
this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
this.semaphore = new Semaphore(this.threads, true);
this.urgent = IrisSettings.get().getPregen().useHighPriority;
this.lastUse = new KMap<>();
}
@@ -63,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return;
}
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
Long lastUseTime = lastUse.get(i);
if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) {
i.unload();
lastUse.remove(i);
long minTime = M.ms() - 10_000;
lastUse.entrySet().removeIf(i -> {
final Chunk chunk = i.getKey();
final Long lastUseTime = i.getValue();
if (!chunk.isLoaded() || lastUseTime == null)
return true;
if (lastUseTime < minTime) {
chunk.unload();
return true;
}
}
return false;
});
world.save();
}).get();
} catch (Throwable e) {
@@ -77,21 +88,6 @@ public class AsyncPregenMethod implements PregeneratorMethod {
}
}
private void completeChunk(int x, int z, PregenListener listener) {
try {
PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> {
lastUse.put(i, M.ms());
listener.onChunkGenerated(x, z);
listener.onChunkCleaned(x, z);
}).get();
} catch (InterruptedException ignored) {
} catch (Throwable e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
@Override
public void init() {
unloadAndSaveAllChunks();
@@ -105,9 +101,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
@Override
public void close() {
semaphore.acquireUninterruptibly(256);
semaphore.acquireUninterruptibly(threads);
unloadAndSaveAllChunks();
burst.close();
executor.shutdown();
resetWorkerThreads();
}
@@ -134,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
} catch (InterruptedException e) {
return;
}
burst.complete(() -> completeChunk(x, z, listener));
executor.generate(x, z, listener);
}
@Override
@@ -146,23 +142,25 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return null;
}
public static void increaseWorkerThreads() {
THREAD_COUNT.updateAndGet(i -> {
if (i > 0) return 1;
var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
try {
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
var pool = field.get(null);
var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length;
var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
if (threads >= adjusted) return 0;
pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted);
return threads;
} catch (ClassNotFoundException ignored) {
} catch (Throwable e) {
Iris.error("Failed to increase worker threads");
e.printStackTrace();
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;
});
@@ -177,12 +175,66 @@ public class AsyncPregenMethod implements PregeneratorMethod {
var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class);
method.invoke(pool, i);
return 0;
} catch (ClassNotFoundException ignored) {
} 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

@@ -377,17 +377,17 @@ public class IrisProject {
KSet<IrisLootTable> loot = new KSet<>();
KSet<IrisBlockData> blocks = new KSet<>();
for (String i : dm.getDimensionLoader().getPossibleKeys()) {
for (String i : dm.getBlockLoader().getPossibleKeys()) {
blocks.add(dm.getBlockLoader().load(i));
}
dimension.getRegions().forEach((i) -> regions.add(dm.getRegionLoader().load(i)));
dimension.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i)));
regions.forEach((i) -> biomes.addAll(i.getAllBiomes(null)));
regions.forEach((i) -> biomes.addAll(i.getAllBiomes(() -> dm)));
regions.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
regions.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
dimension.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp)));
biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(null))));
biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(() -> dm))));
biomes.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
biomes.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
spawners.forEach((i) -> i.getSpawns().forEach((j) -> entities.add(dm.getEntityLoader().load(j.getEntity()))));

View File

@@ -140,6 +140,8 @@ public class SchemaBuilder {
JSONObject property = buildProperty(k, c);
if (property.getBoolean("!required"))
required.put(k.getName());
property.remove("!required");
properties.put(k.getName(), property);
}
@@ -151,19 +153,7 @@ public class SchemaBuilder {
o.put("properties", properties);
if (c.isAnnotationPresent(Snippet.class)) {
JSONObject anyOf = new JSONObject();
JSONArray arr = new JSONArray();
JSONObject str = new JSONObject();
str.put("type", "string");
arr.put(o);
arr.put(str);
anyOf.put("anyOf", arr);
return anyOf;
}
return o;
return buildSnippet(o, c);
}
private JSONObject buildProperty(Field k, Class<?> cl) {
@@ -476,6 +466,26 @@ public class SchemaBuilder {
items.put("$ref", "#/definitions/" + key);
prop.put("items", items);
description.add(SYMBOL_TYPE__N + " Must be a valid Enchantment Type (use ctrl+space for auto complete!)");
} else if (k.isAnnotationPresent(RegistryListFunction.class)) {
var functionClass = k.getDeclaredAnnotation(RegistryListFunction.class).value();
try {
var instance = functionClass.getDeclaredConstructor().newInstance();
String key = instance.key();
fancyType = instance.fancyName();
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
j.put("enum", instance.apply(data));
definitions.put(key, j);
}
JSONObject items = new JSONObject();
items.put("$ref", "#/definitions/" + key);
prop.put("items", items);
description.add(SYMBOL_TYPE__N + " Must be a valid " + fancyType + " (use ctrl+space for auto complete!)");
} catch (Throwable e) {
Iris.error("Could not execute apply method in " + functionClass.getName());
}
} else if (t.type().equals(PotionEffectType.class)) {
fancyType = "List of Potion Effect Types";
String key = "enum-potion-effect-type";
@@ -512,8 +522,16 @@ public class SchemaBuilder {
d.add(fancyType);
d.add(getDescription(k.getType()));
if (k.getType().isAnnotationPresent(Snippet.class)) {
String sm = k.getType().getDeclaredAnnotation(Snippet.class).value();
Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class);
if (snippet == null) {
ArrayType array = k.getType().getDeclaredAnnotation(ArrayType.class);
if (array != null) {
snippet = array.type().getDeclaredAnnotation(Snippet.class);
}
}
if (snippet != null) {
String sm = snippet.value();
d.add(" ");
d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here.");
}
@@ -541,35 +559,36 @@ public class SchemaBuilder {
description.forEach((g) -> d.add(g.trim()));
prop.put("type", type);
prop.put("description", d.toString("\n"));
return buildSnippet(prop, k.getType());
}
if (k.getType().isAnnotationPresent(Snippet.class)) {
JSONObject anyOf = new JSONObject();
JSONArray arr = new JSONArray();
JSONObject str = new JSONObject();
str.put("type", "string");
String key = "enum-snippet-" + k.getType().getDeclaredAnnotation(Snippet.class).value();
str.put("$ref", "#/definitions/" + key);
private JSONObject buildSnippet(JSONObject prop, Class<?> type) {
Snippet snippet = type.getDeclaredAnnotation(Snippet.class);
if (snippet == null) return prop;
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
JSONArray snl = new JSONArray();
data.getPossibleSnippets(k.getType().getDeclaredAnnotation(Snippet.class).value()).forEach(snl::put);
j.put("enum", snl);
definitions.put(key, j);
}
JSONObject anyOf = new JSONObject();
JSONArray arr = new JSONArray();
JSONObject str = new JSONObject();
str.put("type", "string");
String key = "enum-snippet-" + snippet.value();
str.put("$ref", "#/definitions/" + key);
arr.put(prop);
arr.put(str);
prop.put("description", d.toString("\n"));
str.put("description", d.toString("\n"));
anyOf.put("anyOf", arr);
anyOf.put("description", d.toString("\n"));
anyOf.put("!required", k.isAnnotationPresent(Required.class));
return anyOf;
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
JSONArray snl = new JSONArray();
data.getPossibleSnippets(snippet.value()).forEach(snl::put);
j.put("enum", snl);
definitions.put(key, j);
}
return prop;
arr.put(prop);
arr.put(str);
str.put("description", prop.getString("description"));
anyOf.put("anyOf", arr);
anyOf.put("description", prop.getString("description"));
anyOf.put("!required", type.isAnnotationPresent(Required.class));
return anyOf;
}
@NotNull

View File

@@ -2,8 +2,14 @@ package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import io.papermc.lib.PaperLib;
import java.util.concurrent.atomic.AtomicBoolean;
public class IrisSafeguard {
private static final AtomicBoolean sfg = new AtomicBoolean(false);
public static boolean unstablemode = false;
public static boolean warningmode = false;
public static boolean stablemode = false;
@@ -13,12 +19,46 @@ public class IrisSafeguard {
ServerBootSFG.BootCheck();
}
public static void earlySplash() {
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)
public static void splash(boolean early) {
if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
return;
Iris.instance.splash();
UtilsSFG.splash();
if (!sfg.getAndSet(true)) {
Iris.instance.splash();
UtilsSFG.splash();
}
}
public static String mode() {
if (unstablemode) {
return "unstable";
} else if (warningmode) {
return "warning";
} else {
return "stable";
}
}
public static void suggestPaper() {
PaperLib.suggestPaper(Iris.instance);
}
public static KMap<String, Object> asContext() {
KMap<String, Object> m = new KMap<>();
m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace);
m.put("javaVersion", !ServerBootSFG.isCorrectJDK);
m.put("jre", ServerBootSFG.isJRE);
m.put("missingAgent", ServerBootSFG.missingAgent);
m.put("missingDimensionTypes", ServerBootSFG.missingDimensionTypes);
m.put("failedInjection", ServerBootSFG.failedInjection);
m.put("unsupportedVersion", ServerBootSFG.unsuportedversion);
m.put("serverSoftware", !ServerBootSFG.passedserversoftware);
KList<String> incompatiblePlugins = new KList<>();
ServerBootSFG.incompatibilities.forEach((plugin, present) -> {
if (present) incompatiblePlugins.add(plugin);
});
m.put("plugins", incompatiblePlugins);
return m;
}
}

View File

@@ -1,10 +1,15 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.IrisContextInjector;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.misc.ServerProperties;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import javax.tools.JavaCompiler;
@@ -15,10 +20,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.*;
import static com.volmit.iris.Iris.getJavaVersion;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
@@ -31,6 +33,8 @@ public class ServerBootSFG {
public static boolean hasPrivileges = true;
public static boolean unsuportedversion = false;
public static boolean missingDimensionTypes = false;
public static boolean missingAgent = false;
public static boolean failedInjection = false;
protected static boolean safeguardPassed;
public static boolean passedserversoftware = true;
protected static int count;
@@ -45,9 +49,7 @@ public class ServerBootSFG {
Plugin[] plugins = pluginManager.getPlugins();
incompatibilities.clear();
incompatibilities.put("Multiverse-Core", false);
incompatibilities.put("dynmap", false);
incompatibilities.put("TerraformGenerator", false);
incompatibilities.put("Stratos", false);
String pluginName;
@@ -112,10 +114,21 @@ public class ServerBootSFG {
severityMedium++;
}
if (IrisContextInjector.isMissingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
if (!Agent.install()) {
missingAgent = true;
joiner.add("Missing Java Agent");
severityHigh++;
} else {
if (missingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
if (!INMS.get().injectBukkit()) {
failedInjection = true;
joiner.add("Failed Bukkit Injection");
severityHigh++;
}
}
allIncompatibilities = joiner.toString();
@@ -160,17 +173,41 @@ public class ServerBootSFG {
}
public static boolean enoughDiskSpace() {
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
File freeSpace = Bukkit.getWorldContainer();
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
if (gigabytes > 3){
return true;
} else {
return false;
}
return gigabytes > 3;
}
private static boolean checkJavac(String path) {
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
}
private static boolean missingDimensionTypes() {
return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new));
}
private static KSet<String> getDimensionTypes() {
var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
var worlds = bukkit.getConfigurationSection("worlds");
if (worlds == null) return new KSet<>();
var types = new KSet<String>();
for (String world : worlds.getKeys(false)) {
var gen = worlds.getString(world + ".generator");
if (gen == null) continue;
String loadKey;
if (gen.equalsIgnoreCase("iris")) {
loadKey = IrisSettings.get().getGenerator().getDefaultWorldType();
} else if (gen.startsWith("Iris:")) {
loadKey = gen.substring(5);
} else continue;
IrisDimension dimension = Iris.loadDimension(world, loadKey);
if (dimension == null) continue;
types.add(dimension.getDimensionTypeKey());
}
return types;
}
}

View File

@@ -1,6 +1,7 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.format.C;
public class UtilsSFG {
@@ -9,7 +10,9 @@ public class UtilsSFG {
}
public static void printIncompatibleWarnings() {
// String SupportedIrisVersion = getDescription().getVersion(); //todo Automatic version
String[] parts = Iris.instance.getDescription().getVersion().split("-");
String minVersion = parts[1];
String maxVersion = parts[2];
if (ServerBootSFG.safeguardPassed) {
Iris.safeguard(C.BLUE + "0 Conflicts found");
@@ -21,29 +24,29 @@ public class UtilsSFG {
Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found");
}
if (ServerBootSFG.incompatibilities.get("Multiverse-Core")) {
Iris.safeguard(C.RED + "Multiverse");
Iris.safeguard(C.RED + "- The plugin Multiverse is not compatible with the server.");
Iris.safeguard(C.RED + "- If you want to have a world manager, consider using PhantomWorlds or MyWorlds instead.");
}
if (ServerBootSFG.incompatibilities.get("dynmap")) {
Iris.safeguard(C.RED + "Dynmap");
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
}
if (ServerBootSFG.incompatibilities.get("TerraformGenerator") || ServerBootSFG.incompatibilities.get("Stratos")) {
Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos");
if (ServerBootSFG.incompatibilities.get("Stratos")) {
Iris.safeguard(C.YELLOW + "Stratos");
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
}
if (ServerBootSFG.unsuportedversion) {
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 " + minVersion + " > " + maxVersion);
}
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.missingAgent) {
Iris.safeguard(C.RED + "Java Agent");
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
}
if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");

View File

@@ -75,6 +75,10 @@ public class ExternalDataSVC implements IrisService {
if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) {
Iris.info("EcoItems found, loading EcoItemsDataProvider...");
}
providers.add(new KGeneratorsDataProvider());
if (Bukkit.getPluginManager().getPlugin("KGenerators") != null) {
Iris.info("KGenerators found, loading KGeneratorsDataProvider...");
}
for (ExternalDataProvider p : providers) {
if (p.isReady()) {

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

View File

@@ -18,7 +18,6 @@
package com.volmit.iris.core.service;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
@@ -250,30 +249,25 @@ public class StudioSVC implements IrisService {
return;
}
File dimensions = new File(dir, "dimensions");
IrisData data = IrisData.get(dir);
String[] dimensions = data.getDimensionLoader().getPossibleKeys();
if (!(dimensions.exists() && dimensions.isDirectory())) {
sender.sendMessage("Invalid Format. Missing dimensions folder");
return;
}
if (dimensions.listFiles() == null) {
if (dimensions == null || dimensions.length == 0) {
sender.sendMessage("No dimension file found in the extracted zip file.");
sender.sendMessage("Check it is there on GitHub and report this to staff!");
} else if (dimensions.listFiles().length != 1) {
} else if (dimensions.length != 1) {
sender.sendMessage("Dimensions folder must have 1 file in it");
return;
}
File dim = dimensions.listFiles()[0];
IrisDimension d = data.getDimensionLoader().load(dimensions[0]);
if (!dim.isFile()) {
if (d == null) {
sender.sendMessage("Invalid dimension (folder) in dimensions folder");
return;
}
String key = dim.getName().split("\\Q.\\E")[0];
IrisDimension d = new Gson().fromJson(IO.readAll(dim), IrisDimension.class);
String key = d.getLoadKey();
sender.sendMessage("Importing " + d.getName() + " (" + key + ")");
File packEntry = new File(packs, key);

View File

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

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

@@ -199,8 +199,10 @@ public class IrisCreator {
world.get().setTime(6000);
}
});
} else
} else {
addToBukkitYml();
J.s(() -> Iris.linkMultiverseCore.updateWorld(world.get(), dimension));
}
if (pregen != null) {
CompletableFuture<Boolean> ff = new CompletableFuture<>();

View File

@@ -12,7 +12,6 @@ import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import org.bukkit.Bukkit;
import java.io.File;
@@ -24,28 +23,28 @@ import java.util.Collections;
public class IrisPackBenchmarking {
@Getter
public static IrisPackBenchmarking instance;
public static boolean benchmarkInProgress = false;
private static final ThreadLocal<IrisPackBenchmarking> instance = new ThreadLocal<>();
private final PrecisionStopwatch stopwatch = new PrecisionStopwatch();
private final IrisDimension dimension;
private final int radius;
private final boolean gui;
public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) {
instance = this;
this.dimension = dimension;
this.radius = radius;
this.gui = gui;
runBenchmark();
}
public static IrisPackBenchmarking getInstance() {
return instance.get();
}
private void runBenchmark() {
Thread.ofVirtual()
.name("PackBenchmarking")
.start(() -> {
Iris.info("Setting up benchmark environment ");
benchmarkInProgress = true;
IO.delete(new File(Bukkit.getWorldContainer(), "benchmark"));
createBenchmark();
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
@@ -59,10 +58,6 @@ public class IrisPackBenchmarking {
}
public boolean getBenchmarkInProgress() {
return benchmarkInProgress;
}
public void finishedBenchmark(KList<Integer> cps) {
try {
String time = Form.duration((long) stopwatch.getMilliseconds());
@@ -132,13 +127,18 @@ public class IrisPackBenchmarking {
}
private void startBenchmark() {
IrisToolbelt.pregenerate(PregenTask
.builder()
.gui(gui)
.radiusX(radius)
.radiusZ(radius)
.build(), Bukkit.getWorld("benchmark")
);
try {
instance.set(this);
IrisToolbelt.pregenerate(PregenTask
.builder()
.gui(gui)
.radiusX(radius)
.radiusZ(radius)
.build(), Bukkit.getWorld("benchmark")
);
} finally {
instance.remove();
}
}
private double calculateAverage(KList<Integer> list) {

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.pregenerator.PregenTask;
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.service.StudioSVC;
import com.volmit.iris.engine.framework.Engine;
@@ -141,7 +142,18 @@ public class IrisToolbelt {
* @return the pregenerator job (already started)
*/
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

@@ -42,6 +42,7 @@ import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import java.io.File;
import java.util.UUID;
@Data
@@ -108,10 +109,15 @@ public class IrisComplex implements DataProvider {
}
//@builder
engine.getDimension().getRegions().forEach((i) -> data.getRegionLoader().load(i)
.getAllBiomes(this).forEach((b) -> b
.getGenerators()
.forEach((c) -> registerGenerator(c.getCachedGenerator(this)))));
if (focusRegion != null) {
focusRegion.getAllBiomes(this).forEach(this::registerGenerators);
} else {
engine.getDimension()
.getRegions()
.forEach(i -> data.getRegionLoader().load(i)
.getAllBiomes(this)
.forEach(this::registerGenerators));
}
overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream");
engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z)));
rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream()
@@ -245,7 +251,15 @@ public class IrisComplex implements DataProvider {
}
}
return null;
String key = UUID.randomUUID().toString();
IrisRegion region = new IrisRegion();
region.getLandBiomes().add(focus.getLoadKey());
region.getSeaBiomes().add(focus.getLoadKey());
region.getShoreBiomes().add(focus.getLoadKey());
region.setLoadKey(key);
region.setLoader(data);
region.setLoadFile(new File(data.getDataFolder(), data.getRegionLoader().getFolderName() + "/" + key + ".json"));
return region;
}
private IrisDecorator decorateFor(IrisBiome b, double x, double z, IrisDecorationPart part) {
@@ -360,6 +374,10 @@ public class IrisComplex implements DataProvider {
return Math.max(Math.min(getInterpolatedHeight(engine, x, z, seed) + fluidHeight + overlayStream.get(x, z), engine.getHeight()), 0);
}
private void registerGenerators(IrisBiome biome) {
biome.getGenerators().forEach(c -> registerGenerator(c.getCachedGenerator(this)));
}
private void registerGenerator(IrisGenerator cachedGenerator) {
generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator);
}

View File

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

View File

@@ -29,7 +29,9 @@ import com.volmit.iris.engine.mantle.components.MantleJigsawComponent;
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import lombok.*;
import java.io.File;
@@ -44,7 +46,7 @@ public class IrisEngineMantle implements EngineMantle {
@Getter(AccessLevel.NONE)
private final KMap<Integer, KList<MantleComponent>> components;
private final AtomicCache<KList<Pair<KList<MantleComponent>, Integer>>> componentsCache = new AtomicCache<>();
private final AtomicCache<Integer> radCache = new AtomicCache<>();
private final AtomicCache<KSet<MantleFlag>> disabledFlags = new AtomicCache<>();
private final MantleObjectComponent object;
private final MantleJigsawComponent jigsaw;
@@ -101,10 +103,20 @@ public class IrisEngineMantle implements EngineMantle {
@Override
public void registerComponent(MantleComponent c) {
c.setEnabled(!getDimension().getDisabledComponents().contains(c.getFlag()));
components.computeIfAbsent(c.getPriority(), k -> new KList<>()).add(c);
componentsCache.reset();
}
@Override
public KList<MantleFlag> getComponentFlags() {
return components.values()
.stream()
.flatMap(KList::stream)
.map(MantleComponent::getFlag)
.collect(KList.collector());
}
@Override
public MantleJigsawComponent getJigsawComponent() {
return jigsaw;

View File

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

View File

@@ -106,6 +106,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
}
}
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData(), true);
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), true) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), true) : ore;
if (ore != null) {
h.set(xf, i, zf, ore);
continue;
}
if (i > he && i <= hf) {
fdepth = hf - i;
@@ -138,9 +146,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
continue;
}
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData());
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData()) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData()) : ore;
ore = biome.generateOres(realX, i, realZ, rng, getData(), false);
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), false) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), false) : ore;
if (ore != null) {
h.set(xf, i, zf, ore);

View File

@@ -75,9 +75,9 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -140,7 +140,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getTarget().getWorld().minHeight();
}
void setMinHeight(int min);
default void setMinHeight(int min) {
getTarget().getWorld().minHeight(min);
}
@BlockCoordinates
default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException {
@@ -287,76 +289,79 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return;
}
var chunk = mantle.getChunk(c);
if (chunk.isFlagged(MantleFlag.ETCHED)) return;
chunk.flag(MantleFlag.ETCHED, true);
var chunk = mantle.getChunk(c).use();
try {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
int betterY = y + getWorld().minHeight();
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
});
})));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
})));
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
int betterY = y + getWorld().minHeight();
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
});
})));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
})));
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
KMap<Long, Integer> updates = new KMap<>();
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
return;
}
boolean u = false;
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
u = true;
}
if (u) {
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
if (vv != null) {
return Math.max(vv, y);
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
KMap<Long, Integer> updates = new KMap<>();
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
return;
}
boolean u = false;
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
u = true;
}
return y;
if (u) {
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
if (vv != null) {
return Math.max(vv, y);
}
return y;
});
}
});
}
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
int vx = x & 15;
int vz = z & 15;
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
updateLighting(x, y, z, c);
}
}
});
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(0, 20))));
});
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
int vx = x & 15;
int vz = z & 15;
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
updateLighting(x, y, z, c);
}
}
});
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(0, 20))));
try {
semaphore.acquire(3);
} catch (InterruptedException ignored) {}
try {
semaphore.acquire(3);
} catch (InterruptedException ignored) {}
} finally {
chunk.release();
}
}
private static Runnable run(Semaphore semaphore, Runnable runnable) {
@@ -453,14 +458,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
for (int i = 0; i < 4; i++) {
try {
Arrays.parallelSort(nitems, (a, b) -> rng.nextInt());
break;
} catch (Throwable e) {
Iris.reportError(e);
}
for (int i = nitems.length; i > 1; i--) {
int j = rng.nextInt(i);
ItemStack tmp = nitems[i - 1];
nitems[i - 1] = nitems[j];
nitems[j] = tmp;
}
inventory.setContents(nitems);
@@ -852,6 +854,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
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) {
Set<String> regionKeys = getDimension()
.getAllRegions(this).stream()
@@ -872,31 +893,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
if (s.getLoadKey().equals(getDimension().getStronghold())) {
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
if (p.isEmpty()) {
Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
if (pr == null) {
player.sendMessage(C.GOLD + "No strongholds in world.");
}
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) {
} else {
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
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
public void on(WorldUnloadEvent e) {
if (e.getWorld().equals(getTarget().getWorld().realWorld())) {

View File

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

View File

@@ -0,0 +1,14 @@
package com.volmit.iris.engine.mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentFlag {
MantleFlag value();
}

View File

@@ -65,6 +65,8 @@ public interface EngineMantle extends IObjectPlacer {
void registerComponent(MantleComponent c);
KList<MantleFlag> getComponentFlags();
default int getHighest(int x, int z) {
return getHighest(x, z, getData());
}
@@ -227,7 +229,9 @@ public interface EngineMantle extends IObjectPlacer {
}
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
mc.raiseFlag(c.getFlag(), () -> c.generateLayer(writer, x, z, context));
mc.raiseFlag(c.getFlag(), () -> {
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
});
}
@ChunkCoordinates
@@ -289,23 +293,25 @@ public interface EngineMantle extends IObjectPlacer {
}
default void cleanupChunk(int x, int z) {
if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) {
getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> {
getMantle().deleteChunkSlice(x, z, BlockData.class);
getMantle().deleteChunkSlice(x, z, String.class);
getMantle().deleteChunkSlice(x, z, MatterCavern.class);
getMantle().deleteChunkSlice(x, z, MatterFluidBody.class);
if (!isCovered(x, z)) return;
MantleChunk chunk = getMantle().getChunk(x, z).use();
try {
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
chunk.deleteSlices(BlockData.class);
chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class);
chunk.deleteSlices(MatterFluidBody.class);
});
} finally {
chunk.release();
}
}
default long getToUnload(){
return getMantle().getToUnload().size();
default long getUnloadRegionCount() {
return getMantle().getUnloadRegionCount();
}
default long getNotQueuedLoadedRegions(){
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size();
}
default double getTectonicDuration(){
return getMantle().getAdjustedIdleDuration().get();
default double getAdjustedIdleDuration() {
return getMantle().getAdjustedIdleDuration();
}
}

View File

@@ -30,4 +30,5 @@ public abstract class IrisMantleComponent implements MantleComponent {
private final EngineMantle engineMantle;
private final MantleFlag flag;
private final int priority;
private boolean enabled = true;
}

View File

@@ -61,6 +61,10 @@ public interface MantleComponent extends Comparable<MantleComponent> {
MantleFlag getFlag();
boolean isEnabled();
void setEnabled(boolean b);
@ChunkCoordinates
void generateLayer(MantleWriter writer, int x, int z, ChunkContext context);

View File

@@ -60,8 +60,9 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
this.x = x;
this.z = z;
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
int r = radius / 4;
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) {
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
}
}
@@ -143,7 +144,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
if (cx >= this.x - radius && cx <= this.x + radius
&& cz >= this.z - radius && cz <= this.z + radius) {
MantleChunk chunk = cachedChunks.get(Cache.key(cx, cz));
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
if (chunk == null) {
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
@@ -152,6 +153,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
Matter matter = chunk.getOrCreate(y >> 4);
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
} else {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
}
}
@@ -639,9 +642,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
@Override
public void close() {
cachedChunks.values().removeIf(c -> {
c.release();
return true;
});
var iterator = cachedChunks.values().iterator();
while (iterator.hasNext()) {
iterator.next().release();
iterator.remove();
}
}
}

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent;
import com.volmit.iris.engine.mantle.MantleWriter;
@@ -32,6 +33,7 @@ import com.volmit.iris.util.math.RNG;
import lombok.Getter;
@Getter
@ComponentFlag(MantleFlag.CARVED)
public class MantleCarvingComponent extends IrisMantleComponent {
private final int radius = computeRadius();
@@ -58,21 +60,21 @@ public class MantleCarvingComponent extends IrisMantleComponent {
@ChunkCoordinates
private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) {
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4);
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
}
private int computeRadius() {
var dimension = getDimension();
int max = 0;
max = Math.max(max, dimension.getCarving().getMaxRange(getData()));
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
for (var i : dimension.getAllRegions(this::getData)) {
max = Math.max(max, i.getCarving().getMaxRange(getData()));
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
}
for (var i : dimension.getAllBiomes(this::getData)) {
max = Math.max(max, i.getCarving().getMaxRange(getData()));
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
}
return max;

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent;
import com.volmit.iris.engine.mantle.MantleWriter;
@@ -32,6 +33,7 @@ import com.volmit.iris.util.math.RNG;
import lombok.Getter;
@Getter
@ComponentFlag(MantleFlag.FLUID_BODIES)
public class MantleFluidBodyComponent extends IrisMantleComponent {
private final int radius = computeRadius();

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.engine.jigsaw.PlannedStructure;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent;
import com.volmit.iris.engine.mantle.MantleWriter;
@@ -39,6 +40,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
@ComponentFlag(MantleFlag.JIGSAW)
public class MantleJigsawComponent extends IrisMantleComponent {
@Getter
private final int radius = computeRadius();
@@ -93,27 +95,13 @@ public class MantleJigsawComponent extends IrisMantleComponent {
try {
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
return false;
if (!checkBiomes(i, x, z))
return false;
} catch (Throwable ignored) {
}
} catch (Throwable ignored) {}
RNG rng = new RNG(seed);
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
return place(writer, position, structure, rng, false);
}
private boolean checkBiomes(IrisJigsawStructurePlacement placement, int x, int z) {
var biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
if (biome == null) {
return true;
}
var exclude = placement.getExclude();
return !(biome.isSea() && exclude.isExcludeOceanBiomes() ||
biome.isLand() && exclude.isExcludeLandBiomes() ||
biome.isShore() && exclude.isExcludeShoreBiomes());
}
@ChunkCoordinates
private boolean checkMinDistances(KMap<String, Integer> minDistances, int x, int z, KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
int range = 0;

View File

@@ -20,6 +20,7 @@ package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent;
import com.volmit.iris.engine.mantle.MantleWriter;
@@ -47,6 +48,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
@ComponentFlag(MantleFlag.OBJECT)
public class MantleObjectComponent extends IrisMantleComponent {
private final int radius = computeRadius();

View File

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

View File

@@ -52,16 +52,20 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
BurstExecutor burst = burst().burst(multicore);
long seed = x * 341873128712L + z * 132897987541L;
long mask = 0;
for (IrisDepositGenerator k : getDimension().getDeposits()) {
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
for (IrisDepositGenerator k : region.getDeposits()) {
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
for (IrisDepositGenerator k : biome.getDeposits()) {
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
burst.complete();
}
@@ -78,7 +82,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
if (k.getPerClumpSpawnChance() < rng.d())
continue;
IrisObject clump = k.getClump(rng, getData());
IrisObject clump = k.getClump(getEngine(), rng, getData());
int dim = clump.getW();
int min = dim / 2;

View File

@@ -171,12 +171,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@ArrayType(type = IrisOreGenerator.class, min = 1)
private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
if (b != null) {

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.INMS;
@@ -34,8 +35,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import java.util.Map;
@@ -202,6 +203,14 @@ public class IrisBlockData extends IrisRegistrant {
public TileData tryGetTile(IrisData data) {
//TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities.
var type = getBlockData(data).getMaterial();
if (type == Material.SPAWNER && this.data.containsKey("entitySpawn")) {
String id = (String) this.data.get("entitySpawn");
if (tileData == null) tileData = new KMap<>();
KMap<String, Object> spawnData = (KMap<String, Object>) tileData.computeIfAbsent("SpawnData", k -> new KMap<>());
KMap<String, Object> entity = (KMap<String, Object>) spawnData.computeIfAbsent("entity", k -> new KMap<>());
entity.putIfAbsent("id", Identifier.fromString(id).toString());
}
if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty())
return null;
return new TileData(type, this.tileData);

View File

@@ -61,21 +61,25 @@ public class IrisCarving {
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
doCarving(writer, rng, engine, x, y, z, -1);
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
doCarving(writer, rng, engine, x, y, z, depth, -1);
}
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
int nextRecursion = recursion + 1;
if (caves.isNotEmpty()) {
for (IrisCavePlacer i : caves) {
i.generateCave(writer, rng, engine, x, y, z, waterHint);
if (recursion > i.getMaxRecursion()) continue;
i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint);
}
}
if (ravines.isNotEmpty()) {
for (IrisRavinePlacer i : ravines) {
i.generateRavine(writer, rng, engine, x, y, z, waterHint);
if (recursion > i.getMaxRecursion()) continue;
i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint);
}
}
@@ -104,15 +108,18 @@ public class IrisCarving {
}
}
public int getMaxRange(IrisData data) {
public int getMaxRange(IrisData data, int recursion) {
int max = 0;
int nextRecursion = recursion + 1;
for (IrisCavePlacer i : caves) {
max = Math.max(max, i.getSize(data));
if (recursion > i.getMaxRecursion()) continue;
max = Math.max(max, i.getSize(data, nextRecursion));
}
for (IrisRavinePlacer i : ravines) {
max = Math.max(max, i.getSize(data));
if (recursion > i.getMaxRecursion()) continue;
max = Math.max(max, i.getSize(data, nextRecursion));
}
if (elipsoids.isNotEmpty()) {

View File

@@ -66,14 +66,12 @@ public class IrisCave extends IrisRegistrant {
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
generate(writer, rng, engine, x, y, z, -1);
generate(writer, rng, engine, x, y, z, 0, -1, true);
}
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, boolean breakSurface) {
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, breakSurface, girth + 9);
int highestWater = Math.max(waterHint, -1);
if (highestWater == -1) {
@@ -92,7 +90,7 @@ public class IrisCave extends IrisRegistrant {
int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight());
for (IrisPosition i : points) {
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), h);
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
}
MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
@@ -108,7 +106,7 @@ public class IrisCave extends IrisRegistrant {
}
public int getMaxSize(IrisData data) {
return getWorm().getMaxDistance() + fork.getMaxRange(data);
public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
}
}

View File

@@ -50,6 +50,10 @@ public class IrisCavePlacer implements IRare {
@Desc("The cave to place")
@RegistryListResource(IrisCave.class)
private String cave;
@MinNumber(1)
@MaxNumber(256)
@Desc("The maximum recursion depth")
private int maxRecursion = 16;
@Desc("If set to true, this cave is allowed to break the surface")
private boolean breakSurface = true;
@Desc("The height range this cave can spawn at. If breakSurface is false, the output of this range will be clamped by the current world height to prevent surface breaking.")
@@ -60,10 +64,10 @@ public class IrisCavePlacer implements IRare {
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
generateCave(mantle, rng, engine, x, y, z, -1);
generateCave(mantle, rng, engine, x, y, z, 0, -1);
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
if (fail.get()) {
return;
}
@@ -82,28 +86,24 @@ public class IrisCavePlacer implements IRare {
}
if (y == -1) {
if(!breakSurface) {
int eH = engine.getHeight(x, z);
if (caveStartHeight.getMax() > eH) {
caveStartHeight.setMax(eH);
}
}
y = (int) caveStartHeight.get(rng, x, z, data);
int h = (int) caveStartHeight.get(rng, x, z, data);
int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9);
y = Math.min(h, ma);
}
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, breakSurface);
} catch (Throwable e) {
e.printStackTrace();
fail.set(true);
}
}
public int getSize(IrisData data) {
public int getSize(IrisData data, int depth) {
IrisCave cave = getRealCave(data);
if (cave != null) {
return cave.getMaxSize(data);
return cave.getMaxSize(data, depth);
}
return 32;

View File

@@ -1,77 +0,0 @@
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.misc.ServerProperties;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
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;
private final int totalWorlds;
private int worldCounter = 0;
public IrisContextInjector() {
if (!Bukkit.getWorlds().isEmpty()) {
totalWorlds = 0;
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");
int i = 1;
if (Bukkit.getAllowNether()) i++;
if (Bukkit.getAllowEnd()) i++;
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
missingDimensionTypes = true;
totalWorlds = 0;
return;
}
if (overworld || nether || end) {
var pair = INMS.get().injectUncached(overworld, nether, end);
i += pair.getA() - 3;
autoClosing = pair.getB();
}
totalWorlds = i;
instance.registerListener(this);
}
@EventHandler
public void on(WorldInitEvent event) {
if (++worldCounter < totalWorlds) return;
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

@@ -20,6 +20,7 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KSet;
@@ -87,10 +88,10 @@ public class IrisDepositGenerator {
@Desc("Ore varience is how many different objects clumps iris will create")
private int varience = 3;
public IrisObject getClump(RNG rng, IrisData rdata) {
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
KList<IrisObject> objects = this.objects.aquire(() ->
{
RNG rngv = rng.nextParallelRNG(3957778);
RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode());
KList<IrisObject> objectsf = new KList<>();
for (int i = 0; i < varience; i++) {

View File

@@ -25,13 +25,16 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.core.nms.datapack.IDataFixer.Dimension;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.engine.object.annotations.functions.ComponentFlagFunction;
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.io.IO;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.noise.CNG;
@@ -45,8 +48,7 @@ import org.bukkit.Material;
import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData;
import java.io.File;
import java.io.IOException;
import java.io.*;
@Accessors(chain = true)
@AllArgsConstructor
@@ -74,10 +76,6 @@ public class IrisDimension extends IrisRegistrant {
@MaxNumber(2032)
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeight = 256;
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeightEnd = 256;
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeightNether = 256;
@RegistryListResource(IrisJigsawStructure.class)
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
private String stronghold;
@@ -166,10 +164,8 @@ public class IrisDimension extends IrisRegistrant {
private int fluidHeight = 63;
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
private IrisRange dimensionHeight = new IrisRange(-64, 320);
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
private IrisRange dimensionHeightEnd = new IrisRange(-64, 320);
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
private IrisRange dimensionHeightNether = new IrisRange(-64, 320);
@Desc("Define options for this dimension")
private IrisDimensionTypeOptions dimensionOptions = new IrisDimensionTypeOptions();
@RegistryListResource(IrisBiome.class)
@Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.")
private String focus = "";
@@ -244,6 +240,10 @@ public class IrisDimension extends IrisRegistrant {
@MaxNumber(318)
@Desc("The Subterrain Fluid Layer Height")
private int caveLavaHeight = 8;
@RegistryListFunction(ComponentFlagFunction.class)
@ArrayType(type = MantleFlag.class)
@Desc("Collection of disabled components")
private KList<MantleFlag> disabledComponents = new KList<>();
public int getMaxHeight() {
return (int) getDimensionHeight().getMax();
@@ -253,12 +253,14 @@ public class IrisDimension extends IrisRegistrant {
return (int) getDimensionHeight().getMin();
}
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
if (b != null) {
@@ -410,6 +412,39 @@ public class IrisDimension extends IrisRegistrant {
});
}
public Dimension getBaseDimension() {
return switch (getEnvironment()) {
case NETHER -> Dimension.NETHER;
case THE_END -> Dimension.END;
default -> Dimension.OVERWORLD;
};
}
public String getDimensionTypeKey() {
return getDimensionType().key();
}
public IrisDimensionType getDimensionType() {
return new IrisDimensionType(getBaseDimension(), getDimensionOptions(), getLogicalHeight(), getMaxHeight() - getMinHeight(), getMinHeight());
}
public void installDimensionType(IDataFixer fixer, KList<File> folders) {
IrisDimensionType type = getDimensionType();
String json = type.toJson(fixer);
Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + type.key() + '"');
for (File datapacks : folders) {
File output = new File(datapacks, "iris/data/iris/dimension_type/" + type.key() + ".json");
output.getParentFile().mkdirs();
try {
IO.writeAll(output, json);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
}
}
@Override
public String getFolderName() {
return "dimensions";
@@ -426,11 +461,12 @@ public class IrisDimension extends IrisRegistrant {
}
public static void writeShared(KList<File> folders, DimensionHeight height) {
Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\"");
Iris.verbose(" Installing Data Pack Vanilla Dimension Types");
String[] jsonStrings = height.jsonStrings();
for (File datapacks : folders) {
write(datapacks, "overworld", height.overworldType());
write(datapacks, "the_nether", height.netherType());
write(datapacks, "the_end", height.endType());
write(datapacks, "overworld", jsonStrings[0]);
write(datapacks, "the_nether", jsonStrings[1]);
write(datapacks, "the_end", jsonStrings[2]);
}
String raw = """
@@ -455,17 +491,9 @@ public class IrisDimension extends IrisRegistrant {
}
private static void write(File datapacks, String type, String json) {
File dimType = new File(datapacks, "iris/data/iris/dimension_type/" + type + ".json");
if (json == null) return;
File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json");
dimType.getParentFile().mkdirs();
try {
IO.writeAll(dimType, json);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
dimTypeVanilla.getParentFile().mkdirs();
try {

View File

@@ -0,0 +1,91 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.io.IO;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.*;
@Getter
@ToString
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode
public final class IrisDimensionType {
@NonNull
private final String key;
@NonNull
private final IDataFixer.Dimension base;
@NonNull
private final IrisDimensionTypeOptions options;
private final int logicalHeight;
private final int height;
private final int minY;
public IrisDimensionType(
@NonNull IDataFixer.Dimension base,
@NonNull IrisDimensionTypeOptions options,
int logicalHeight,
int height,
int minY
) {
if (logicalHeight > height) throw new IllegalArgumentException("Logical height cannot be greater than height");
if (logicalHeight < 0) throw new IllegalArgumentException("Logical height cannot be less than zero");
if (height < 16 || height > 4064 ) throw new IllegalArgumentException("Height must be between 16 and 4064");
if ((height & 15) != 0) throw new IllegalArgumentException("Height must be a multiple of 16");
if (minY < -2032 || minY > 2031) throw new IllegalArgumentException("Min Y must be between -2032 and 2031");
if ((minY & 15) != 0) throw new IllegalArgumentException("Min Y must be a multiple of 16");
this.base = base;
this.options = options;
this.logicalHeight = logicalHeight;
this.height = height;
this.minY = minY;
this.key = createKey();
}
public static IrisDimensionType fromKey(String key) {
var stream = new ByteArrayInputStream(IO.decode(key.replace(".", "=").toUpperCase()));
try (var din = new DataInputStream(stream)) {
return new IrisDimensionType(
IDataFixer.Dimension.values()[din.readUnsignedByte()],
new IrisDimensionTypeOptions().read(din),
Varint.readUnsignedVarInt(din),
Varint.readUnsignedVarInt(din),
Varint.readSignedVarInt(din)
);
} catch (IOException e) {
throw new RuntimeException("This is impossible", e);
}
}
public String toJson(IDataFixer fixer) {
return fixer.createDimension(
base,
minY,
height,
logicalHeight,
options.copy()
).toString(4);
}
private String createKey() {
var stream = new ByteArrayOutputStream(41);
try (var dos = new DataOutputStream(stream)) {
dos.writeByte(base.ordinal());
options.write(dos);
Varint.writeUnsignedVarInt(logicalHeight, dos);
Varint.writeUnsignedVarInt(height, dos);
Varint.writeSignedVarInt(minY, dos);
} catch (IOException e) {
throw new RuntimeException("This is impossible", e);
}
return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase();
}
public IrisDimensionTypeOptions options() {
return options.copy();
}
}

View File

@@ -0,0 +1,320 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.MaxNumber;
import com.volmit.iris.engine.object.annotations.MinNumber;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.json.JSONObject;
import lombok.*;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
@Data
@NoArgsConstructor
@Accessors(fluent = true, chain = true)
@Desc("Optional options for the dimension type")
public class IrisDimensionTypeOptions {
@MinNumber(0.00001)
@MaxNumber(30000000)
@Desc("The multiplier applied to coordinates when leaving the dimension. Value between 0.00001 and 30000000.0 (both inclusive).")
private double coordinateScale = -1;
@MinNumber(0)
@MaxNumber(1)
@Desc("How much light the dimension has. When set to 0, it completely follows the light level; when set to 1, there is no ambient lighting.")
private float ambientLight = -1;
@Nullable
@MinNumber(0)
@MaxNumber(Long.MAX_VALUE)
@Desc("If this is set to an int, the time of the day is the specified value. To ensure a normal time cycle, set it to null.")
private Long fixedTime = -1L;
@Nullable
@MinNumber(-2032)
@MaxNumber(2031)
@Desc("Optional value between -2032 and 2031. If set, determines the lower edge of the clouds. If set to null, clouds are disabled in the dimension.")
private Integer cloudHeight = -1;
@MinNumber(0)
@MaxNumber(15)
@Desc("Value between 0 and 15 (both inclusive). Maximum block light required when the monster spawns.")
private int monsterSpawnBlockLightLimit = -1;
@NonNull
@Desc("Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. Also lets stalactites drip lava and causes lava to spread faster and thinner.")
private TriState ultrawarm = DEFAULT;
@NonNull
@Desc("When false, compasses spin randomly, and using a bed to set the respawn point or sleep, is disabled. When true, nether portals can spawn zombified piglins, and creaking hearts can spawn creakings.")
private TriState natural = DEFAULT;
@NonNull
@Desc("When false, Piglins and hoglins shake and transform to zombified entities.")
private TriState piglinSafe = DEFAULT;
@NonNull
@Desc("When false, the respawn anchor blows up when trying to set spawn point.")
private TriState respawnAnchorWorks = DEFAULT;
@NonNull
@Desc("When false, the bed blows up when trying to sleep.")
private TriState bedWorks = DEFAULT;
@NonNull
@Desc("Whether players with the Bad Omen effect can cause a raid.")
private TriState raids = DEFAULT;
@NonNull
@Desc("Whether the dimension has skylight or not.")
private TriState skylight = DEFAULT;
@NonNull
@Desc("Whether the dimension has a bedrock ceiling. Note that this is only a logical ceiling. It is unrelated with whether the dimension really has a block ceiling.")
private TriState ceiling = DEFAULT;
public IrisDimensionTypeOptions(
@NonNull TriState ultrawarm,
@NonNull TriState natural,
@NonNull TriState piglinSafe,
@NonNull TriState respawnAnchorWorks,
@NonNull TriState bedWorks,
@NonNull TriState raids,
@NonNull TriState skylight,
@NonNull TriState ceiling,
double coordinateScale,
float ambientLight,
@Nullable Long fixedTime,
@Nullable Integer cloudHeight,
int monsterSpawnBlockLightLimit
) {
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
this.ultrawarm = ultrawarm;
this.natural = natural;
this.piglinSafe = piglinSafe;
this.respawnAnchorWorks = respawnAnchorWorks;
this.bedWorks = bedWorks;
this.raids = raids;
this.skylight = skylight;
this.ceiling = ceiling;
this.coordinateScale = coordinateScale;
this.ambientLight = ambientLight;
this.fixedTime = fixedTime;
this.cloudHeight = cloudHeight;
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
}
public IrisDimensionTypeOptions coordinateScale(double coordinateScale) {
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
this.coordinateScale = coordinateScale;
return this;
}
public IrisDimensionTypeOptions ambientLight(float ambientLight) {
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
this.ambientLight = ambientLight;
return this;
}
public IrisDimensionTypeOptions cloudHeight(@Nullable Integer cloudHeight) {
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
this.cloudHeight = cloudHeight;
return this;
}
public IrisDimensionTypeOptions monsterSpawnBlockLightLimit(int monsterSpawnBlockLightLimit) {
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
return this;
}
public void write(DataOutput dos) throws IOException {
int bits = 0;
int index = 0;
for (TriState state : new TriState[]{
ultrawarm,
natural,
skylight,
ceiling,
piglinSafe,
bedWorks,
respawnAnchorWorks,
raids
}) {
if (state == DEFAULT) {
index++;
continue;
}
bits |= (short) (1 << index++);
if (state == TRUE)
bits |= (short) (1 << index++);
}
if (coordinateScale != -1)
bits |= (1 << index++);
if (ambientLight != -1)
bits |= (1 << index++);
if (monsterSpawnBlockLightLimit != -1)
bits |= (1 << index++);
if (fixedTime != null) {
bits |= (1 << index++);
if (fixedTime != -1L)
bits |= (1 << index++);
}
if (cloudHeight != null) {
bits |= (1 << index++);
if (cloudHeight != -1)
bits |= (1 << index);
}
Varint.writeSignedVarInt(bits, dos);
if (coordinateScale != -1)
Varint.writeUnsignedVarLong(Double.doubleToLongBits(coordinateScale), dos);
if (ambientLight != -1)
Varint.writeUnsignedVarInt(Float.floatToIntBits(ambientLight), dos);
if (monsterSpawnBlockLightLimit != -1)
Varint.writeSignedVarInt(monsterSpawnBlockLightLimit, dos);
if (fixedTime != null && fixedTime != -1L)
Varint.writeSignedVarLong(fixedTime, dos);
if (cloudHeight != null && cloudHeight != -1)
Varint.writeSignedVarInt(cloudHeight, dos);
}
public IrisDimensionTypeOptions read(DataInput dis) throws IOException {
TriState[] states = new TriState[8];
Arrays.fill(states, DEFAULT);
int bits = Varint.readSignedVarInt(dis);
int index = 0;
for (int i = 0; i < 8; i++) {
if ((bits & (1 << index++)) == 0)
continue;
states[i] = (bits & (1 << index++)) == 0 ? FALSE : TRUE;
}
ultrawarm = states[0];
natural = states[1];
skylight = states[2];
ceiling = states[3];
piglinSafe = states[4];
bedWorks = states[5];
respawnAnchorWorks = states[6];
raids = states[7];
coordinateScale = (bits & (1 << index++)) != 0 ? Double.longBitsToDouble(Varint.readUnsignedVarLong(dis)) : -1;
ambientLight = (bits & (1 << index++)) != 0 ? Float.intBitsToFloat(Varint.readUnsignedVarInt(dis)) : -1;
monsterSpawnBlockLightLimit = (bits & (1 << index++)) != 0 ? Varint.readSignedVarInt(dis) : -1;
fixedTime = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarLong(dis) : -1L : null;
cloudHeight = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarInt(dis) : -1 : null;
return this;
}
public IrisDimensionTypeOptions resolve(IrisDimensionTypeOptions other) {
if (ultrawarm == DEFAULT)
ultrawarm = other.ultrawarm;
if (natural == DEFAULT)
natural = other.natural;
if (piglinSafe == DEFAULT)
piglinSafe = other.piglinSafe;
if (respawnAnchorWorks == DEFAULT)
respawnAnchorWorks = other.respawnAnchorWorks;
if (bedWorks == DEFAULT)
bedWorks = other.bedWorks;
if (raids == DEFAULT)
raids = other.raids;
if (skylight == DEFAULT)
skylight = other.skylight;
if (ceiling == DEFAULT)
ceiling = other.ceiling;
if (coordinateScale == -1)
coordinateScale = other.coordinateScale;
if (ambientLight == -1)
ambientLight = other.ambientLight;
if (fixedTime != null && fixedTime == -1L)
fixedTime = other.fixedTime;
if (cloudHeight != null && cloudHeight == -1)
cloudHeight = other.cloudHeight;
if (monsterSpawnBlockLightLimit == -1)
monsterSpawnBlockLightLimit = other.monsterSpawnBlockLightLimit;
return this;
}
public JSONObject toJson() {
if (!isComplete()) throw new IllegalStateException("Cannot serialize incomplete options");
JSONObject json = new JSONObject();
json.put("ultrawarm", ultrawarm.bool());
json.put("natural", natural.bool());
json.put("piglin_safe", piglinSafe.bool());
json.put("respawn_anchor_works", respawnAnchorWorks.bool());
json.put("bed_works", bedWorks.bool());
json.put("has_raids", raids.bool());
json.put("has_skylight", skylight.bool());
json.put("has_ceiling", ceiling.bool());
json.put("coordinate_scale", coordinateScale);
json.put("ambient_light", ambientLight);
json.put("monster_spawn_block_light_limit", monsterSpawnBlockLightLimit);
if (fixedTime != null) json.put("fixed_time", fixedTime);
if (cloudHeight != null) json.put("cloud_height", cloudHeight);
return json;
}
public IrisDimensionTypeOptions copy() {
return new IrisDimensionTypeOptions(
ultrawarm,
natural,
piglinSafe,
respawnAnchorWorks,
bedWorks,
raids,
skylight,
ceiling,
coordinateScale,
ambientLight,
fixedTime,
cloudHeight,
monsterSpawnBlockLightLimit
);
}
public boolean isComplete() {
return ultrawarm != DEFAULT
&& natural != DEFAULT
&& piglinSafe != DEFAULT
&& respawnAnchorWorks != DEFAULT
&& bedWorks != DEFAULT
&& raids != DEFAULT
&& skylight != DEFAULT
&& ceiling != DEFAULT
&& coordinateScale != -1
&& ambientLight != -1
&& monsterSpawnBlockLightLimit != -1
&& (fixedTime == null || fixedTime != -1L)
&& (cloudHeight == null || cloudHeight != -1);
}
@Desc("Allows reusing the behavior of the base dimension")
public enum TriState {
@Desc("Follow the behavior of the base dimension")
DEFAULT,
@Desc("True")
TRUE,
@Desc("False")
FALSE;
public boolean bool() {
return this == TRUE;
}
}
}

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

View File

@@ -123,6 +123,8 @@ public class IrisImage extends IrisRegistrant {
}
public void writeDebug(IrisImageChannel channel) {
try {
File at = new File(getLoadFile().getParentFile(), "debug-see-" + getLoadFile().getName());
BufferedImage b = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);

View File

@@ -78,9 +78,6 @@ public class IrisJigsawStructurePlacement implements IRare {
@Desc("Threshold for noise style")
private double threshold = 0.5;
@Desc("Exclude the structure from certain places")
private IrisPlacementExclusion exclude = new IrisPlacementExclusion();
@ArrayType(type = IrisJigsawMinDistance.class)
@Desc("List of minimum distances to check for")
private KList<IrisJigsawMinDistance> minDistances = new KList<>();

View File

@@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant {
private boolean emptyAbove = true;
@Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.")
private double exhaustionChance = 0.33;
private double exhaustionChance = 0;
public boolean shouldExhaust() {
return RNG.r.chance(exhaustionChance);
return exhaustionChance > RNG.r.nextDouble();
}
@Override

View File

@@ -1,33 +0,0 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.util.collection.KList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents an image map")
@Data
public class IrisPlacementExclusion {
@Desc("Excludes the structure from all ocean biomes defined in region")
private boolean excludeOceanBiomes;
@Desc("Excludes the structure from all shore biomes defined in region")
private boolean excludeShoreBiomes;
@Desc("Excludes the structure from all land biomes defined in region")
private boolean excludeLandBiomes;
@RegistryListResource(IrisBiome.class)
@Desc("tbd")
@ArrayType(min = 1, type = String.class)
private KList<String> excludeBiomes = new KList<>();
}

View File

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

View File

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

View File

@@ -151,12 +151,14 @@ public class IrisRegion extends IrisRegistrant implements IRare {
@ArrayType(type = IrisOreGenerator.class, min = 1)
private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
if (b != null) {

View File

@@ -62,7 +62,7 @@ public class IrisWorm {
private IrisStyledRange girth = new IrisStyledRange().setMin(3).setMax(5)
.setStyle(new IrisGeneratorStyle(NoiseStyle.PERLIN));
public KList<IrisPosition> generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, Consumer<IrisPosition> fork) {
public KList<IrisPosition> generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, boolean breakSurface, double distance) {
int itr = maxIterations;
double jx, jy, jz;
double cx = x;
@@ -72,12 +72,11 @@ public class IrisWorm {
KList<IrisPosition> pos = new KList<>();
KSet<IrisPosition> check = allowLoops ? null : new KSet<>();
CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gy = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gz = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
while (itr-- > 0) {
IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
fork.accept(current);
pos.add(current);
if (check != null) {
@@ -92,6 +91,10 @@ public class IrisWorm {
cz += jz;
IrisPosition next = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
if (!breakSurface && writer.getEngineMantle().getHighest(next.getX(), next.getZ(), true) <= next.getY() + distance) {
break;
}
if (verticalRange != null && !verticalRange.contains(next.getY())) {
break;
}

View File

@@ -0,0 +1,37 @@
package com.volmit.iris.engine.object.annotations.functions;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.MantleComponent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.mantle.MantleFlag;
import java.util.Objects;
public class ComponentFlagFunction implements ListFunction<KList<String>> {
@Override
public String key() {
return "component-flag";
}
@Override
public String fancyName() {
return "Component Flag";
}
@Override
public KList<String> apply(IrisData data) {
var engine = data.getEngine();
if (engine != null) return engine.getMantle().getComponentFlags().toStringList();
return Iris.getClasses("com.volmit.iris.engine.mantle.components", ComponentFlag.class)
.stream()
.filter(MantleComponent.class::isAssignableFrom)
.map(c -> c.getDeclaredAnnotation(ComponentFlag.class))
.filter(Objects::nonNull)
.map(ComponentFlag::value)
.map(MantleFlag::toString)
.collect(KList.collector());
}
}

View File

@@ -19,10 +19,12 @@
package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.data.chunk.TerrainChunk;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
@@ -39,6 +41,7 @@ import com.volmit.iris.util.io.ReactiveFolder;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
import io.papermc.lib.PaperLib;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
@@ -48,6 +51,7 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.generator.BiomeProvider;
@@ -58,7 +62,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
@@ -84,12 +87,13 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private Engine engine;
private Looper hotloader;
private StudioMode lastMode;
private DummyBiomeProvider dummyBiomeProvider;
private final AtomicCache<EngineTarget> targetCache = new AtomicCache<>();
private volatile Engine engine;
private volatile Looper hotloader;
private volatile StudioMode lastMode;
private volatile DummyBiomeProvider dummyBiomeProvider;
@Setter
private StudioGenerator studioGenerator;
private volatile StudioGenerator studioGenerator;
private boolean initialized = false;
@@ -108,84 +112,96 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
}
private static Field getField(Class clazz, String fieldName)
throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldInit(WorldInitEvent event) {
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
world.setRawWorldSeed(event.getWorld().getSeed());
if (initialize(event.getWorld())) return;
Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying...");
J.s(() -> {
if (!initialize(event.getWorld())) {
Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!");
}
}
}, 10);
}
@EventHandler
public void onWorldInit(WorldInitEvent event) {
private boolean initialize(World world) {
Engine engine = getEngine(world);
if (engine == null) return false;
try {
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
world.setRawWorldSeed(event.getWorld().getSeed());
Engine engine = getEngine(event.getWorld());
if (engine == null) {
Iris.warn("Failed to get Engine!");
J.s(() -> {
Engine engine1 = getEngine(event.getWorld());
if (engine1 != null) {
try {
INMS.get().inject(event.getWorld().getSeed(), engine1, event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
initialized = true;
} catch (Throwable e) {
e.printStackTrace();
}
}
}, 10);
} else {
INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld()));
initialized = true;
}
INMS.get().inject(world.getSeed(), engine, world);
Iris.info("Injected Iris Biome Source into " + world.getName());
} catch (Throwable e) {
Iris.reportError(e);
Iris.error("Failed to inject biome source into " + world.getName());
e.printStackTrace();
}
spawnChunks.complete(INMS.get().getSpawnChunkCount(world));
Iris.instance.unregisterListener(this);
initialized = true;
IrisWorlds.get().put(world.getName(), dimensionKey);
return true;
}
@Nullable
@Override
public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) {
Location location = new Location(world, 0, 64, 0);
PaperLib.getChunkAtAsync(location)
.thenAccept(c -> {
World w = c.getWorld();
if (!w.getSpawnLocation().equals(location))
return;
w.setSpawnLocation(location.add(0, w.getHighestBlockYAt(location) - 64, 0));
});
return location;
}
private void setupEngine() {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(getTarget(), studio);
populators.clear();
targetCache.reset();
}
if (dimension == null) {
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
@NotNull
@Override
public EngineTarget getTarget() {
if (engine != null) return engine.getTarget();
if (test != null) {
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile());
Iris.warn("Attempted to install into " + data.getDataFolder().getPath());
data.dump();
data.clearLists();
test = data.getDimensionLoader().load(dimensionKey);
return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
if (test != null) {
Iris.success("Woo! Patched the Engine!");
dimension = test;
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile());
Iris.warn("Attempted to install into " + data.getDataFolder().getPath());
data.dump();
data.clearLists();
test = data.getDimensionLoader().load(dimensionKey);
if (test != null) {
Iris.success("Woo! Patched the Engine!");
dimension = test;
} else {
Iris.error("Failed to patch dimension!");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
}
} else {
Iris.error("Failed to patch dimension!");
Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
}
} else {
Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
}
}
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio);
populators.clear();
return new EngineTarget(world, dimension, data);
});
}
@Override
@@ -297,7 +313,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
hotloader.interrupt();
}
getEngine().close();
final Engine engine = getEngine();
if (engine != null && !engine.isClosed())
engine.close();
folder.clear();
populators.clear();

View File

@@ -24,21 +24,23 @@ import com.volmit.iris.engine.framework.EngineTarget;
import com.volmit.iris.engine.framework.Hotloadable;
import com.volmit.iris.util.data.DataProvider;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
@Nullable
Engine getEngine();
@Override
default IrisData getData() {
return getEngine().getData();
return getTarget().getData();
}
default EngineTarget getTarget() {
return getEngine().getTarget();
}
@NotNull
EngineTarget getTarget();
void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs);

View File

@@ -0,0 +1,46 @@
package com.volmit.iris.util.agent;
import com.volmit.iris.Iris;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class Agent {
private static final String NAME = "com.volmit.iris.util.agent.Installer";
public static final File AGENT_JAR = new File(Iris.instance.getDataFolder(), "agent.jar");
public static ClassReloadingStrategy installed() {
return ClassReloadingStrategy.of(getInstrumentation());
}
public static Instrumentation getInstrumentation() {
Instrumentation instrumentation = doGetInstrumentation();
if (instrumentation == null) throw new IllegalStateException("The agent is not initialized or unavailable");
return instrumentation;
}
public static boolean install() {
if (doGetInstrumentation() != null)
return true;
try {
Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
Iris.info("Installing Java Agent...");
ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE);
} catch (Throwable e) {
e.printStackTrace();
}
return doGetInstrumentation() != null;
}
private static Instrumentation doGetInstrumentation() {
try {
return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null);
} catch (Exception ex) {
return null;
}
}
}

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