mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-06-18 14:50:57 +00:00
kts gone
This commit is contained in:
+268
@@ -0,0 +1,268 @@
|
|||||||
|
import de.undercouch.gradle.tasks.download.Download
|
||||||
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
|
import org.gradle.api.tasks.Copy
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = uri('https://jitpack.io')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath('com.github.VolmitSoftware:NMSTools:c88961416f')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'java-library'
|
||||||
|
alias(libs.plugins.download)
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'art.arcane'
|
||||||
|
version = '4.0.0-1.21.11'
|
||||||
|
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||||
|
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||||
|
.get()
|
||||||
|
|
||||||
|
apply plugin: 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/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins')
|
||||||
|
registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins')
|
||||||
|
registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins')
|
||||||
|
// ==============================================================
|
||||||
|
|
||||||
|
def nmsBindings = [
|
||||||
|
v1_21_R7: '1.21.11-R0.1-SNAPSHOT',
|
||||||
|
]
|
||||||
|
Class nmsTypeClass = Class.forName('NMSBinding$Type')
|
||||||
|
nmsBindings.each { key, value ->
|
||||||
|
project(":nms:${key}") {
|
||||||
|
apply plugin: JavaPlugin
|
||||||
|
|
||||||
|
def nmsConfig = new Config()
|
||||||
|
nmsConfig.jvm = 21
|
||||||
|
nmsConfig.version = value
|
||||||
|
nmsConfig.type = Enum.valueOf(nmsTypeClass, 'DIRECT')
|
||||||
|
extensions.extraProperties.set('nms', nmsConfig)
|
||||||
|
plugins.apply(NMSBinding)
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(project(':core'))
|
||||||
|
compileOnly(volmLibCoordinate) {
|
||||||
|
changing = true
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
compileOnly(rootProject.libs.annotations)
|
||||||
|
compileOnly(rootProject.libs.byteBuddy.core)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def included = configurations.create('included')
|
||||||
|
def jarJar = configurations.create('jarJar')
|
||||||
|
dependencies {
|
||||||
|
nmsBindings.keySet().each { key ->
|
||||||
|
add('included', project(path: ":nms:${key}", configuration: 'reobf'))
|
||||||
|
}
|
||||||
|
add('included', project(path: ':core', configuration: 'shadow'))
|
||||||
|
add('jarJar', project(':core:agent'))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('jar', Jar).configure {
|
||||||
|
inputs.files(included)
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
from(jarJar, provider { included.resolve().collect { zipTree(it) } })
|
||||||
|
archiveFileName.set("Iris-${project.version}.jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('iris', Copy) {
|
||||||
|
group = 'iris'
|
||||||
|
dependsOn('jar')
|
||||||
|
from(layout.buildDirectory.file("libs/Iris-${project.version}.jar"))
|
||||||
|
into(layout.buildDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('irisDev', Copy) {
|
||||||
|
group = 'iris'
|
||||||
|
from(project(':core').layout.buildDirectory.files('libs/core-javadoc.jar', 'libs/core-sources.jar'))
|
||||||
|
rename { String fileName -> fileName.replace('core', "Iris-${project.version}") }
|
||||||
|
into(layout.buildDirectory)
|
||||||
|
dependsOn(':core:sourcesJar')
|
||||||
|
dependsOn(':core:javadocJar')
|
||||||
|
}
|
||||||
|
|
||||||
|
def cli = file('sentry-cli.exe')
|
||||||
|
tasks.register('downloadCli', Download) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('release') {
|
||||||
|
group = 'io.sentry'
|
||||||
|
dependsOn('downloadCli')
|
||||||
|
doLast {
|
||||||
|
String url = 'http://sentry.volmit.com:8080'
|
||||||
|
def authToken = project.findProperty('sentry.auth.token') ?: System.getenv('SENTRY_AUTH_TOKEN')
|
||||||
|
String org = 'sentry'
|
||||||
|
String projectName = 'iris'
|
||||||
|
runCommand(cli, '--url', url, '--auth-token', authToken, 'releases', 'new', '-o', org, '-p', projectName, version)
|
||||||
|
runCommand(cli, '--url', url, '--auth-token', authToken, 'releases', 'set-commits', '-o', org, '-p', projectName, version, '--auto', '--ignore-missing')
|
||||||
|
//exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-o", org, "-p", projectName, version)
|
||||||
|
cli.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void runCommand(Object... command) {
|
||||||
|
Process process = new ProcessBuilder(command.collect { it.toString() }).start()
|
||||||
|
process.inputStream.readLines().each { println(it) }
|
||||||
|
process.errorStream.readLines().each { println(it) }
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.configureEach {
|
||||||
|
resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
|
||||||
|
resolutionStrategy.cacheDynamicVersionsFor(0, 'seconds')
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven { url = uri('https://repo.papermc.io/repository/maven-public/') }
|
||||||
|
maven { url = uri('https://repo.codemc.org/repository/maven-public/') }
|
||||||
|
|
||||||
|
maven { url = uri('https://jitpack.io') } // EcoItems, score
|
||||||
|
maven { url = uri('https://repo.nexomc.com/releases/') } // nexo
|
||||||
|
maven { url = uri('https://maven.devs.beer/') } // itemsadder
|
||||||
|
maven { url = uri('https://repo.extendedclip.com/releases/') } // placeholderapi
|
||||||
|
maven { url = uri('https://mvn.lumine.io/repository/maven-public/') } // mythic
|
||||||
|
maven { url = uri('https://nexus.phoenixdevt.fr/repository/maven-public/') } //MMOItems
|
||||||
|
maven { url = uri('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.named('compileJava', JavaCompile).configure {
|
||||||
|
options.compilerArgs.add('-parameters')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.debugOptions.debugLevel = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('javadoc').configure {
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.quiet()
|
||||||
|
//options.addStringOption("Xdoclint:none") // TODO: Re-enable this
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('sourcesJar', Jar) {
|
||||||
|
archiveClassifier.set('sources')
|
||||||
|
from(sourceSets.main.allSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('javadocJar', Jar) {
|
||||||
|
archiveClassifier.set('javadoc')
|
||||||
|
from(tasks.named('javadoc').map { it.destinationDir })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) {
|
||||||
|
System.err.println()
|
||||||
|
System.err.println('=========================================================================================================')
|
||||||
|
System.err.println('You must run gradle on Java 21 or newer. You are using ' + JavaVersion.current())
|
||||||
|
System.err.println()
|
||||||
|
System.err.println('=== For IDEs ===')
|
||||||
|
System.err.println('1. Configure the project for Java 21 toolchain')
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerCustomOutputTask(String name, String path) {
|
||||||
|
if (!System.getProperty('os.name').toLowerCase().contains('windows')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("build${name}", Copy) {
|
||||||
|
group = 'development'
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
dependsOn('iris')
|
||||||
|
from(layout.buildDirectory.file("Iris-${project.version}.jar"))
|
||||||
|
into(file(path))
|
||||||
|
rename { String ignored -> 'Iris.jar' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerCustomOutputTaskUnix(String name, String path) {
|
||||||
|
if (System.getProperty('os.name').toLowerCase().contains('windows')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("build${name}", Copy) {
|
||||||
|
group = 'development'
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
dependsOn('iris')
|
||||||
|
from(layout.buildDirectory.file("Iris-${project.version}.jar"))
|
||||||
|
into(file(path))
|
||||||
|
rename { String ignored -> 'Iris.jar' }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
import de.undercouch.gradle.tasks.download.Download
|
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|
||||||
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:c88961416f")
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
java
|
|
||||||
`java-library`
|
|
||||||
alias(libs.plugins.download)
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "art.arcane"
|
|
||||||
version = "4.0.0-1.21.11"
|
|
||||||
val volmLibCoordinate: String = providers.gradleProperty("volmLibCoordinate")
|
|
||||||
.orElse("com.github.VolmitSoftware:VolmLib:master-SNAPSHOT")
|
|
||||||
.get()
|
|
||||||
|
|
||||||
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/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins")
|
|
||||||
registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
|
|
||||||
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
|
|
||||||
// ==============================================================
|
|
||||||
|
|
||||||
val nmsBindings = mapOf(
|
|
||||||
"v1_21_R7" to "1.21.11-R0.1-SNAPSHOT",
|
|
||||||
)
|
|
||||||
nmsBindings.forEach { (key, value) ->
|
|
||||||
project(":nms:$key") {
|
|
||||||
apply<JavaPlugin>()
|
|
||||||
|
|
||||||
nmsBinding {
|
|
||||||
jvm = 21
|
|
||||||
version = value
|
|
||||||
type = NMSBinding.Type.DIRECT
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(project(":core"))
|
|
||||||
compileOnly(volmLibCoordinate) {
|
|
||||||
isChanging = true
|
|
||||||
isTransitive = false
|
|
||||||
}
|
|
||||||
compileOnly(rootProject.libs.annotations)
|
|
||||||
compileOnly(rootProject.libs.byteBuddy.core)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val included: Configuration by configurations.creating
|
|
||||||
val jarJar: Configuration by configurations.creating
|
|
||||||
dependencies {
|
|
||||||
for (key in nmsBindings.keys) {
|
|
||||||
included(project(":nms:$key", "reobf"))
|
|
||||||
}
|
|
||||||
included(project(":core", "shadow"))
|
|
||||||
jarJar(project(":core:agent"))
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
jar {
|
|
||||||
inputs.files(included)
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
from(jarJar, provider { included.resolve().map(::zipTree) })
|
|
||||||
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 url = "http://sentry.volmit.com:8080"
|
|
||||||
val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN")
|
|
||||||
val org = "sentry"
|
|
||||||
val projectName = "iris"
|
|
||||||
exec(cli, "--url", url , "--auth-token", authToken, "releases", "new", "-o", org, "-p", projectName, version)
|
|
||||||
exec(cli, "--url", url , "--auth-token", authToken, "releases", "set-commits", "-o", org, "-p", projectName, version, "--auto", "--ignore-missing")
|
|
||||||
//exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-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(0, "seconds")
|
|
||||||
resolutionStrategy.cacheDynamicVersionsFor(0, "seconds")
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
apply<JavaPlugin>()
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(JavaLanguageVersion.of(21))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
options.debugOptions.debugLevel = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
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().isCompatibleWith(JavaVersion.VERSION_21)) {
|
|
||||||
System.err.println()
|
|
||||||
System.err.println("=========================================================================================================")
|
|
||||||
System.err.println("You must run gradle on Java 21 or newer. You are using " + JavaVersion.current())
|
|
||||||
System.err.println()
|
|
||||||
System.err.println("=== For IDEs ===")
|
|
||||||
System.err.println("1. Configure the project for Java 21 toolchain")
|
|
||||||
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" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven {
|
||||||
|
url = uri('https://jitpack.io')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation('org.ow2.asm:asm:9.8')
|
||||||
|
implementation('com.github.VolmitSoftware:NMSTools:c88961416f')
|
||||||
|
implementation('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.18')
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm") version embeddedKotlinVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(JavaLanguageVersion.of(21))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(21)
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(JvmTarget.JVM_21)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
maven("https://jitpack.io")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("org.ow2.asm:asm:9.8")
|
|
||||||
implementation("com.github.VolmitSoftware:NMSTools:c88961416f")
|
|
||||||
implementation("io.papermc.paperweight:paperweight-userdev:2.0.0-beta.18")
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
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.api.tasks.TaskProvider;
|
||||||
|
import org.gradle.jvm.tasks.Jar;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
|
||||||
|
public class ApiGenerator implements Plugin<Project> {
|
||||||
|
@Override
|
||||||
|
public void apply(Project target) {
|
||||||
|
target.getPlugins().apply("maven-publish");
|
||||||
|
TaskProvider<GenerateApiTask> task = target.getTasks().register("irisApi", GenerateApiTask.class);
|
||||||
|
|
||||||
|
PublishingExtension publishing = target.getExtensions().findByType(PublishingExtension.class);
|
||||||
|
if (publishing == null) {
|
||||||
|
throw new GradleException("Publishing extension not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing.getRepositories().maven(repository -> {
|
||||||
|
repository.setName("deployDir");
|
||||||
|
repository.setUrl(targetDirectory(target).toURI());
|
||||||
|
});
|
||||||
|
|
||||||
|
publishing.getPublications().create("maven", MavenPublication.class, publication -> {
|
||||||
|
publication.setGroupId(target.getName());
|
||||||
|
publication.setVersion(target.getVersion().toString());
|
||||||
|
publication.artifact(task);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File targetDirectory(Project project) {
|
||||||
|
String dir = System.getenv("DEPLOY_DIR");
|
||||||
|
if (dir == null) {
|
||||||
|
return project.getLayout().getBuildDirectory().dir("api").get().getAsFile();
|
||||||
|
}
|
||||||
|
return new File(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GenerateApiTask extends DefaultTask {
|
||||||
|
private final File inputFile;
|
||||||
|
private final File outputFile;
|
||||||
|
|
||||||
|
public GenerateApiTask() {
|
||||||
|
setGroup("iris");
|
||||||
|
dependsOn("jar");
|
||||||
|
finalizedBy("publishMavenPublicationToDeployDirRepository");
|
||||||
|
doLast(task -> getLogger().lifecycle("The API is located at " + getOutputFile().getAbsolutePath()));
|
||||||
|
|
||||||
|
TaskProvider<Jar> jarTask = getProject().getTasks().named("jar", Jar.class);
|
||||||
|
this.inputFile = jarTask.get().getArchiveFile().get().getAsFile();
|
||||||
|
this.outputFile = ApiGenerator.targetDirectory(getProject()).toPath().resolve(this.inputFile.getName()).toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public File getInputFile() {
|
||||||
|
return inputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getOutputFile() {
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void generate() throws IOException {
|
||||||
|
File parent = outputFile.getParentFile();
|
||||||
|
if (parent != null) {
|
||||||
|
parent.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (JarFile jar = new JarFile(inputFile);
|
||||||
|
JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) {
|
||||||
|
jar.stream()
|
||||||
|
.parallel()
|
||||||
|
.filter(entry -> !entry.isDirectory())
|
||||||
|
.filter(entry -> entry.getName().endsWith(".class"))
|
||||||
|
.forEach(entry -> writeStrippedClass(jar, out, entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeStrippedClass(JarFile jar, JarOutputStream out, JarEntry entry) {
|
||||||
|
byte[] bytes;
|
||||||
|
try (InputStream input = jar.getInputStream(entry)) {
|
||||||
|
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||||
|
ClassVisitor visitor = new MethodClearingVisitor(writer);
|
||||||
|
ClassReader reader = new ClassReader(input);
|
||||||
|
reader.accept(visitor, 0);
|
||||||
|
bytes = writer.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (out) {
|
||||||
|
try {
|
||||||
|
JarEntry outputEntry = new JarEntry(entry.getName());
|
||||||
|
out.putNextEntry(outputEntry);
|
||||||
|
out.write(bytes);
|
||||||
|
out.closeEntry();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MethodClearingVisitor extends ClassVisitor {
|
||||||
|
public MethodClearingVisitor(ClassVisitor cv) {
|
||||||
|
super(Opcodes.ASM9, cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
|
return new ExceptionThrowingMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExceptionThrowingMethodVisitor extends MethodVisitor {
|
||||||
|
public ExceptionThrowingMethodVisitor(MethodVisitor mv) {
|
||||||
|
super(Opcodes.ASM9, mv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
public class Config {
|
||||||
|
public int jvm = 21;
|
||||||
|
public NMSBinding.Type type = NMSBinding.Type.DIRECT;
|
||||||
|
public String version;
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
import com.volmit.nmstools.NMSToolsExtension;
|
||||||
|
import com.volmit.nmstools.NMSToolsPlugin;
|
||||||
|
import io.papermc.paperweight.userdev.PaperweightUser;
|
||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension;
|
||||||
|
import io.papermc.paperweight.userdev.PaperweightUserExtension;
|
||||||
|
import io.papermc.paperweight.userdev.attribute.Obfuscation;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.Named;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.attributes.Bundling;
|
||||||
|
import org.gradle.api.attributes.Category;
|
||||||
|
import org.gradle.api.attributes.LibraryElements;
|
||||||
|
import org.gradle.api.attributes.Usage;
|
||||||
|
import org.gradle.api.file.FileTree;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.plugins.ExtraPropertiesExtension;
|
||||||
|
import org.gradle.api.provider.Provider;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion;
|
||||||
|
import org.gradle.jvm.toolchain.JavaToolchainService;
|
||||||
|
import org.gradle.work.DisableCachingByDefault;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static io.papermc.paperweight.util.constants.ConstantsKt.REOBF_CONFIG;
|
||||||
|
|
||||||
|
public class NMSBinding implements Plugin<Project> {
|
||||||
|
private static final String NEW_LINE = System.lineSeparator();
|
||||||
|
private static final byte[] NEW_LINE_BYTES = NEW_LINE.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project target) {
|
||||||
|
ExtraPropertiesExtension extra = target.getExtensions().getExtraProperties();
|
||||||
|
Object configValue = extra.has("nms") ? extra.get("nms") : null;
|
||||||
|
if (!(configValue instanceof Config)) {
|
||||||
|
throw new GradleException("No NMS binding configuration found");
|
||||||
|
}
|
||||||
|
|
||||||
|
Config config = (Config) configValue;
|
||||||
|
int jvm = config.jvm;
|
||||||
|
Type type = config.type;
|
||||||
|
|
||||||
|
if (type == Type.USER_DEV) {
|
||||||
|
target.getPlugins().apply(PaperweightUser.class);
|
||||||
|
|
||||||
|
PaperweightUserDependenciesExtension dependenciesExtension =
|
||||||
|
target.getDependencies().getExtensions().findByType(PaperweightUserDependenciesExtension.class);
|
||||||
|
if (dependenciesExtension != null) {
|
||||||
|
dependenciesExtension.paperDevBundle(config.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
JavaPluginExtension java = target.getExtensions().findByType(JavaPluginExtension.class);
|
||||||
|
if (java == null) {
|
||||||
|
throw new GradleException("Java plugin not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
java.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(jvm));
|
||||||
|
JavaToolchainService javaToolchains = target.getExtensions().getByType(JavaToolchainService.class);
|
||||||
|
target.getExtensions().configure(PaperweightUserExtension.class,
|
||||||
|
extension -> extension.getJavaLauncher().set(javaToolchains.launcherFor(java.getToolchain())));
|
||||||
|
} else {
|
||||||
|
extra.set("nmsTools.useBuildTools", type == Type.BUILD_TOOLS);
|
||||||
|
target.getPlugins().apply(NMSToolsPlugin.class);
|
||||||
|
target.getExtensions().configure(NMSToolsExtension.class, extension -> {
|
||||||
|
extension.getJvm().set(jvm);
|
||||||
|
extension.getVersion().set(config.version);
|
||||||
|
});
|
||||||
|
|
||||||
|
ObjectFactory objects = target.getObjects();
|
||||||
|
target.getConfigurations().register(REOBF_CONFIG, configuration -> {
|
||||||
|
configuration.setCanBeConsumed(true);
|
||||||
|
configuration.setCanBeResolved(false);
|
||||||
|
configuration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, named(objects, Usage.class, Usage.JAVA_RUNTIME));
|
||||||
|
configuration.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, named(objects, Category.class, Category.LIBRARY));
|
||||||
|
configuration.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(objects, LibraryElements.class, LibraryElements.JAR));
|
||||||
|
configuration.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, named(objects, Bundling.class, Bundling.EXTERNAL));
|
||||||
|
configuration.getAttributes().attribute(Obfuscation.Companion.getOBFUSCATION_ATTRIBUTE(), named(objects, Obfuscation.class, Obfuscation.OBFUSCATED));
|
||||||
|
configuration.getOutgoing().artifact(target.getTasks().named("remap"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] version = parseVersion(config.version);
|
||||||
|
int major = version[0];
|
||||||
|
int minor = version[1];
|
||||||
|
if (major <= 20 && minor <= 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.getTasks().register("convert", ConversionTask.class, type);
|
||||||
|
target.getTasks().named("compileJava").configure(task -> task.dependsOn("convert"));
|
||||||
|
target.getRootProject().getTasks()
|
||||||
|
.matching(task -> task.getName().equals("prepareKotlinBuildScriptModel"))
|
||||||
|
.configureEach(task -> task.dependsOn(target.getPath() + ":convert"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void nmsBinding(Project project, Action<Config> action) {
|
||||||
|
Config config = new Config();
|
||||||
|
action.execute(config);
|
||||||
|
project.getExtensions().getExtraProperties().set("nms", config);
|
||||||
|
project.getPlugins().apply(NMSBinding.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] parseVersion(String version) {
|
||||||
|
String trimmed = version;
|
||||||
|
int suffix = trimmed.indexOf('-');
|
||||||
|
if (suffix >= 0) {
|
||||||
|
trimmed = trimmed.substring(0, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = trimmed.split("\\.");
|
||||||
|
return new int[]{Integer.parseInt(parts[1]), Integer.parseInt(parts[2])};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Named> T named(ObjectFactory objects, Class<T> type, String name) {
|
||||||
|
return objects.named(type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisableCachingByDefault
|
||||||
|
public abstract static class ConversionTask extends DefaultTask {
|
||||||
|
private final Pattern pattern;
|
||||||
|
private final String replacement;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ConversionTask(Type type) {
|
||||||
|
setGroup("nms");
|
||||||
|
getInputs().property("type", type);
|
||||||
|
|
||||||
|
JavaPluginExtension java = getProject().getExtensions().findByType(JavaPluginExtension.class);
|
||||||
|
if (java == null) {
|
||||||
|
throw new GradleException("Java plugin not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider<FileTree> source = java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getAllJava);
|
||||||
|
getInputs().files(source);
|
||||||
|
getOutputs().files(source);
|
||||||
|
|
||||||
|
if (type == Type.USER_DEV) {
|
||||||
|
this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\." + getProject().getName());
|
||||||
|
this.replacement = "org.bukkit.craftbukkit";
|
||||||
|
} else {
|
||||||
|
this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\.(?!" + getProject().getName() + ")");
|
||||||
|
this.replacement = "org.bukkit.craftbukkit." + getProject().getName() + ".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void process() {
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(16);
|
||||||
|
try {
|
||||||
|
Set<File> files = getInputs().getFiles().getFiles();
|
||||||
|
List<Future<?>> futures = new ArrayList<>(files.size());
|
||||||
|
for (File file : files) {
|
||||||
|
if (!file.getName().endsWith(".java")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
futures.add(executor.submit(() -> processFile(file)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Future<?> future : futures) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException(e.getCause());
|
||||||
|
} finally {
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processFile(File file) {
|
||||||
|
List<String> output = new ArrayList<>();
|
||||||
|
boolean changed = false;
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.startsWith("package") || line.isBlank()) {
|
||||||
|
output.add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.startsWith("import")) {
|
||||||
|
if (!changed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher matcher = pattern.matcher(line);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
output.add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.add(matcher.replaceAll(replacement));
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (hasTrailingNewLine(file)) {
|
||||||
|
output.add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
|
||||||
|
for (int i = 0; i < output.size(); i++) {
|
||||||
|
writer.append(output.get(i));
|
||||||
|
if (i + 1 < output.size()) {
|
||||||
|
writer.append(NEW_LINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasTrailingNewLine(File file) throws IOException {
|
||||||
|
if (NEW_LINE_BYTES.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||||
|
if (raf.length() < NEW_LINE_BYTES.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = new byte[NEW_LINE_BYTES.length];
|
||||||
|
raf.seek(raf.length() - bytes.length);
|
||||||
|
raf.readFully(bytes);
|
||||||
|
return Arrays.equals(bytes, NEW_LINE_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
USER_DEV,
|
||||||
|
BUILD_TOOLS,
|
||||||
|
DIRECT
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
import NMSBinding.Type
|
|
||||||
import com.volmit.nmstools.NMSToolsExtension
|
|
||||||
import com.volmit.nmstools.NMSToolsPlugin
|
|
||||||
import io.papermc.paperweight.userdev.PaperweightUser
|
|
||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
import io.papermc.paperweight.userdev.PaperweightUserExtension
|
|
||||||
import io.papermc.paperweight.userdev.attribute.Obfuscation
|
|
||||||
import io.papermc.paperweight.util.constants.REOBF_CONFIG
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.gradle.api.*
|
|
||||||
import org.gradle.api.attributes.Bundling
|
|
||||||
import org.gradle.api.attributes.Category
|
|
||||||
import org.gradle.api.attributes.LibraryElements
|
|
||||||
import org.gradle.api.attributes.Usage
|
|
||||||
import org.gradle.api.model.ObjectFactory
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
|
||||||
import org.gradle.api.tasks.TaskAction
|
|
||||||
import org.gradle.internal.extensions.core.extra
|
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|
||||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
|
||||||
import org.gradle.work.DisableCachingByDefault
|
|
||||||
import java.io.RandomAccessFile
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class NMSBinding : Plugin<Project> {
|
|
||||||
override fun apply(target: Project): Unit = with(target) {
|
|
||||||
val config = extra["nms"] as? Config ?: throw GradleException("No NMS binding configuration found")
|
|
||||||
val jvm = config.jvm
|
|
||||||
val type = config.type
|
|
||||||
|
|
||||||
if (type == Type.USER_DEV) {
|
|
||||||
plugins.apply(PaperweightUser::class.java)
|
|
||||||
dependencies.extensions.findByType(PaperweightUserDependenciesExtension::class.java)
|
|
||||||
?.paperDevBundle(config.version)
|
|
||||||
|
|
||||||
val java = extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
|
|
||||||
java.toolchain.languageVersion.set(JavaLanguageVersion.of(jvm))
|
|
||||||
|
|
||||||
val javaToolchains = project.extensions.getByType(JavaToolchainService::class.java) ?: throw GradleException("Java toolchain service not found")
|
|
||||||
extensions.configure(PaperweightUserExtension::class.java) {
|
|
||||||
it.javaLauncher.set(javaToolchains.launcherFor(java.toolchain))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extra["nmsTools.useBuildTools"] = type == Type.BUILD_TOOLS
|
|
||||||
plugins.apply(NMSToolsPlugin::class.java)
|
|
||||||
extensions.configure(NMSToolsExtension::class.java) {
|
|
||||||
it.jvm.set(jvm)
|
|
||||||
it.version.set(config.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations.register(REOBF_CONFIG) { conf ->
|
|
||||||
conf.isCanBeConsumed = true
|
|
||||||
conf.isCanBeResolved = false
|
|
||||||
conf.attributes {
|
|
||||||
it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
|
||||||
it.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
|
|
||||||
it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
|
||||||
it.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
|
|
||||||
it.attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED))
|
|
||||||
}
|
|
||||||
conf.outgoing.artifact(tasks.named("remap"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val (major, minor) = config.version.parseVersion()
|
|
||||||
if (major <= 20 && minor <= 4) return@with
|
|
||||||
tasks.register("convert", ConversionTask::class.java, type)
|
|
||||||
tasks.named("compileJava") { it.dependsOn("convert") }
|
|
||||||
rootProject.tasks.named("prepareKotlinBuildScriptModel") { it.dependsOn("$path:convert") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@DisableCachingByDefault
|
|
||||||
abstract class ConversionTask @Inject constructor(type: Type) : DefaultTask() {
|
|
||||||
private val pattern: Regex
|
|
||||||
private val replacement: String
|
|
||||||
|
|
||||||
init {
|
|
||||||
group = "nms"
|
|
||||||
inputs.property("type", type)
|
|
||||||
val java = project.extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
|
|
||||||
val source = java.sourceSets.named("main").map { it.allJava }
|
|
||||||
inputs.files(source)
|
|
||||||
outputs.files(source)
|
|
||||||
|
|
||||||
if (type == Type.USER_DEV) {
|
|
||||||
pattern = "org\\.bukkit\\.craftbukkit\\.${project.name}".toRegex()
|
|
||||||
replacement = "org.bukkit.craftbukkit"
|
|
||||||
} else {
|
|
||||||
pattern = "org\\.bukkit\\.craftbukkit\\.(?!${project.name})".toRegex()
|
|
||||||
replacement = "org.bukkit.craftbukkit.${project.name}."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun process() {
|
|
||||||
val dispatcher = Dispatchers.IO.limitedParallelism(16)
|
|
||||||
runBlocking {
|
|
||||||
for (file in inputs.files) {
|
|
||||||
if (file.extension !in listOf("java"))
|
|
||||||
continue
|
|
||||||
|
|
||||||
launch(dispatcher) {
|
|
||||||
val output = ArrayList<String>()
|
|
||||||
var changed = false
|
|
||||||
|
|
||||||
file.bufferedReader().use {
|
|
||||||
for (line in it.lines()) {
|
|
||||||
if (line.startsWith("package") || line.isBlank()) {
|
|
||||||
output += line
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!line.startsWith("import")) {
|
|
||||||
if (!changed) return@launch
|
|
||||||
else {
|
|
||||||
output += line
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!line.contains(pattern)) {
|
|
||||||
output += line
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
output += line.replace(pattern, replacement)
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
RandomAccessFile(file, "r").use { raf ->
|
|
||||||
val bytes = ByteArray(NEW_LINE_BYTES.size)
|
|
||||||
raf.seek(raf.length() - bytes.size)
|
|
||||||
raf.readFully(bytes)
|
|
||||||
if (bytes.contentEquals(NEW_LINE_BYTES))
|
|
||||||
output += ""
|
|
||||||
}
|
|
||||||
|
|
||||||
file.writer().use {
|
|
||||||
val iterator = output.iterator()
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
it.append(iterator.next())
|
|
||||||
if (iterator.hasNext())
|
|
||||||
it.append(NEW_LINE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Type {
|
|
||||||
USER_DEV,
|
|
||||||
BUILD_TOOLS,
|
|
||||||
DIRECT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val NEW_LINE = System.lineSeparator()
|
|
||||||
private val NEW_LINE_BYTES = NEW_LINE.encodeToByteArray()
|
|
||||||
private fun String.parseVersion() = substringBefore('-').split(".").let {
|
|
||||||
it[1].toInt() to it[2].toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Config(
|
|
||||||
var jvm: Int = 21,
|
|
||||||
var type: Type = Type.DIRECT
|
|
||||||
) {
|
|
||||||
lateinit var version: String
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Project.nmsBinding(action: Config.() -> Unit) {
|
|
||||||
extra["nms"] = Config().apply(action)
|
|
||||||
plugins.apply(NMSBinding::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : Named> ObjectFactory.named(name: String): T = named(T::class.java, name)
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('jar', Jar).configure {
|
||||||
|
manifest.attributes(
|
||||||
|
'Agent-Class': 'art.arcane.iris.util.project.agent.Installer',
|
||||||
|
'Premain-Class': 'art.arcane.iris.util.project.agent.Installer',
|
||||||
|
'Can-Redefine-Classes': true,
|
||||||
|
'Can-Retransform-Classes': true
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
plugins {
|
|
||||||
java
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.jar {
|
|
||||||
manifest.attributes(
|
|
||||||
"Agent-Class" to "art.arcane.iris.util.project.agent.Installer",
|
|
||||||
"Premain-Class" to "art.arcane.iris.util.project.agent.Installer",
|
|
||||||
"Can-Redefine-Classes" to true,
|
|
||||||
"Can-Retransform-Classes" to true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
import io.github.slimjar.resolver.data.Mirror
|
||||||
|
import org.ajoberstar.grgit.Grgit
|
||||||
|
import org.gradle.api.tasks.Copy
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
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 {
|
||||||
|
id 'java'
|
||||||
|
id 'java-library'
|
||||||
|
alias(libs.plugins.shadow)
|
||||||
|
alias(libs.plugins.sentry)
|
||||||
|
alias(libs.plugins.slimjar)
|
||||||
|
alias(libs.plugins.grgit)
|
||||||
|
alias(libs.plugins.kotlin.jvm)
|
||||||
|
alias(libs.plugins.kotlin.lombok)
|
||||||
|
}
|
||||||
|
|
||||||
|
def apiVersion = '1.21'
|
||||||
|
def mainClass = 'art.arcane.iris.Iris'
|
||||||
|
def lib = 'art.arcane.iris.util'
|
||||||
|
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||||
|
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||||
|
.get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
compileOnly(libs.multiverseCore)
|
||||||
|
|
||||||
|
// Shaded
|
||||||
|
implementation('de.crazydev22.slimjar.helper:spigot:2.1.5')
|
||||||
|
implementation(volmLibCoordinate) {
|
||||||
|
changing = true
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.commons.math3)
|
||||||
|
slim(libs.oshi)
|
||||||
|
slim(libs.lz4)
|
||||||
|
slim(libs.fastutil)
|
||||||
|
slim(libs.lru)
|
||||||
|
slim(libs.zip)
|
||||||
|
slim(libs.gson)
|
||||||
|
slim(libs.asm)
|
||||||
|
slim(libs.caffeine)
|
||||||
|
slim(libs.byteBuddy.core)
|
||||||
|
slim(libs.byteBuddy.agent)
|
||||||
|
slim(libs.dom4j)
|
||||||
|
slim(libs.jaxen)
|
||||||
|
|
||||||
|
// Script Engine
|
||||||
|
slim(libs.kotlin.stdlib)
|
||||||
|
slim(libs.kotlin.coroutines)
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(21)
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget.set(JvmTarget.JVM_21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sentry {
|
||||||
|
url = 'http://sentry.volmit.com:8080'
|
||||||
|
autoInstallation.enabled = false
|
||||||
|
includeSourceContext = true
|
||||||
|
|
||||||
|
org = 'sentry'
|
||||||
|
projectName = 'iris'
|
||||||
|
authToken = findProperty('sentry.auth.token') as String ?: System.getenv('SENTRY_AUTH_TOKEN')
|
||||||
|
}
|
||||||
|
|
||||||
|
slimJar {
|
||||||
|
mirrors = [
|
||||||
|
new 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")
|
||||||
|
relocate('org.apache.maven', "${lib}.maven")
|
||||||
|
relocate('org.codehaus.plexus', "${lib}.plexus")
|
||||||
|
relocate('org.eclipse.sisu', "${lib}.sisu")
|
||||||
|
relocate('org.eclipse.aether', "${lib}.aether")
|
||||||
|
relocate('com.google.inject', "${lib}.guice")
|
||||||
|
relocate('org.dom4j', "${lib}.dom4j")
|
||||||
|
relocate('org.jaxen', "${lib}.jaxen")
|
||||||
|
relocate('com.github.benmanes.caffeine', "${lib}.caffeine")
|
||||||
|
}
|
||||||
|
|
||||||
|
def embeddedAgentJar = project(':core:agent').tasks.named('jar', Jar)
|
||||||
|
def templateSource = file('src/main/templates')
|
||||||
|
def templateDest = layout.buildDirectory.dir('generated/sources/templates')
|
||||||
|
def generateTemplates = tasks.register('generateTemplates', Copy) {
|
||||||
|
inputs.properties([
|
||||||
|
environment: providers.provider {
|
||||||
|
if (project.hasProperty('release')) {
|
||||||
|
return 'production'
|
||||||
|
}
|
||||||
|
if (project.hasProperty('argghh')) {
|
||||||
|
return 'Argghh!'
|
||||||
|
}
|
||||||
|
return 'development'
|
||||||
|
},
|
||||||
|
commit: providers.provider {
|
||||||
|
String commitId = null
|
||||||
|
Exception failure = null
|
||||||
|
try {
|
||||||
|
commitId = project.extensions.getByType(Grgit).head().id
|
||||||
|
} catch (Exception ex) {
|
||||||
|
failure = ex
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commitId != null && commitId.length() == 40) {
|
||||||
|
return commitId
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error('Git commit hash not found', failure)
|
||||||
|
return 'unknown'
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
from(templateSource)
|
||||||
|
into(templateDest)
|
||||||
|
rename { String fileName -> "art/arcane/iris/${fileName}" }
|
||||||
|
expand(inputs.properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('compileJava', JavaCompile).configure {
|
||||||
|
/**
|
||||||
|
* We need parameter meta for the decree command system
|
||||||
|
*/
|
||||||
|
options.compilerArgs.add('-parameters')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.debugOptions.debugLevel = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('processResources').configure {
|
||||||
|
/**
|
||||||
|
* Expand properties into plugin yml
|
||||||
|
*/
|
||||||
|
def pluginProperties = [
|
||||||
|
name : rootProject.name,
|
||||||
|
version : rootProject.version,
|
||||||
|
apiVersion: apiVersion,
|
||||||
|
main : mainClass,
|
||||||
|
]
|
||||||
|
inputs.properties(pluginProperties)
|
||||||
|
filesMatching('**/plugin.yml') {
|
||||||
|
expand(pluginProperties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar).configure {
|
||||||
|
dependsOn(embeddedAgentJar)
|
||||||
|
mergeServiceFiles()
|
||||||
|
//minimize()
|
||||||
|
relocate('io.github.slimjar', "${lib}.slimjar")
|
||||||
|
exclude('modules/loader-agent.isolated-jar')
|
||||||
|
from(embeddedAgentJar.map { it.archiveFile }) {
|
||||||
|
rename { String ignored -> 'agent.jar' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('sentryCollectSourcesJava').configure {
|
||||||
|
dependsOn(generateTemplates)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('generateSentryBundleIdJava').configure {
|
||||||
|
dependsOn(generateTemplates)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.tasks.matching {
|
||||||
|
it.name == 'prepareKotlinBuildScriptModel'
|
||||||
|
}.configureEach {
|
||||||
|
dependsOn(generateTemplates)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir(generateTemplates.map { it.outputs })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
import io.github.slimjar.func.slimjarHelper
|
|
||||||
import io.github.slimjar.resolver.data.Mirror
|
|
||||||
import org.ajoberstar.grgit.Grgit
|
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|
||||||
import org.gradle.jvm.tasks.Jar
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
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)
|
|
||||||
alias(libs.plugins.grgit)
|
|
||||||
alias(libs.plugins.kotlin.jvm)
|
|
||||||
alias(libs.plugins.kotlin.lombok)
|
|
||||||
}
|
|
||||||
|
|
||||||
val apiVersion = "1.21"
|
|
||||||
val main = "art.arcane.iris.Iris"
|
|
||||||
val lib = "art.arcane.iris.util"
|
|
||||||
val volmLibCoordinate: String = providers.gradleProperty("volmLibCoordinate")
|
|
||||||
.orElse("com.github.VolmitSoftware:VolmLib:master-SNAPSHOT")
|
|
||||||
.get()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(slimjarHelper("spigot"))
|
|
||||||
implementation(volmLibCoordinate) {
|
|
||||||
isChanging = true
|
|
||||||
isTransitive = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.commons.math3)
|
|
||||||
slim(libs.oshi)
|
|
||||||
slim(libs.lz4)
|
|
||||||
slim(libs.fastutil)
|
|
||||||
slim(libs.lru)
|
|
||||||
slim(libs.zip)
|
|
||||||
slim(libs.gson)
|
|
||||||
slim(libs.asm)
|
|
||||||
slim(libs.caffeine)
|
|
||||||
slim(libs.byteBuddy.core)
|
|
||||||
slim(libs.byteBuddy.agent)
|
|
||||||
slim(libs.dom4j)
|
|
||||||
slim(libs.jaxen)
|
|
||||||
|
|
||||||
// Script Engine
|
|
||||||
slim(libs.kotlin.stdlib)
|
|
||||||
slim(libs.kotlin.coroutines)
|
|
||||||
slim(libs.kotlin.scripting.common)
|
|
||||||
slim(libs.kotlin.scripting.jvm)
|
|
||||||
slim(libs.kotlin.scripting.jvm.host)
|
|
||||||
slim(libs.kotlin.scripting.dependencies.maven) {
|
|
||||||
constraints {
|
|
||||||
slim(libs.mavenCore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
disableAutoTargetJvm()
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(JavaLanguageVersion.of(21))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(21)
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(JvmTarget.JVM_21)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sentry {
|
|
||||||
url = "http://sentry.volmit.com:8080"
|
|
||||||
autoInstallation.enabled = false
|
|
||||||
includeSourceContext = true
|
|
||||||
|
|
||||||
org = "sentry"
|
|
||||||
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")
|
|
||||||
relocate("org.apache.maven", "$lib.maven")
|
|
||||||
relocate("org.codehaus.plexus", "$lib.plexus")
|
|
||||||
relocate("org.eclipse.sisu", "$lib.sisu")
|
|
||||||
relocate("org.eclipse.aether", "$lib.aether")
|
|
||||||
relocate("com.google.inject", "$lib.guice")
|
|
||||||
relocate("org.dom4j", "$lib.dom4j")
|
|
||||||
relocate("org.jaxen", "$lib.jaxen")
|
|
||||||
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
|
|
||||||
}
|
|
||||||
|
|
||||||
val embeddedAgentJar = project(":core:agent").tasks.named<Jar>("jar")
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
/**
|
|
||||||
* We need parameter meta for the decree command system
|
|
||||||
*/
|
|
||||||
compileJava {
|
|
||||||
options.compilerArgs.add("-parameters")
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
options.debugOptions.debugLevel = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
dependsOn(embeddedAgentJar)
|
|
||||||
mergeServiceFiles()
|
|
||||||
//minimize()
|
|
||||||
relocate("io.github.slimjar", "$lib.slimjar")
|
|
||||||
exclude("modules/loader-agent.isolated-jar")
|
|
||||||
from(embeddedAgentJar.map { it.archiveFile }) {
|
|
||||||
rename { "agent.jar" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sentryCollectSourcesJava {
|
|
||||||
dependsOn(generateTemplates)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val templateSource = file("src/main/templates")
|
|
||||||
val templateDest = layout.buildDirectory.dir("generated/sources/templates")!!
|
|
||||||
val generateTemplates = tasks.register<Copy>("generateTemplates") {
|
|
||||||
inputs.properties(
|
|
||||||
"environment" to when {
|
|
||||||
project.hasProperty("release") -> "production"
|
|
||||||
project.hasProperty("argghh") -> "Argghh!"
|
|
||||||
else -> "development"
|
|
||||||
},
|
|
||||||
"commit" to provider {
|
|
||||||
val res = runCatching { project.extensions.getByType<Grgit>().head().id }
|
|
||||||
res.getOrDefault("")
|
|
||||||
.takeIf { it.length == 40 } ?: run {
|
|
||||||
this.logger.error("Git commit hash not found", res.exceptionOrNull())
|
|
||||||
"unknown"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
from(templateSource)
|
|
||||||
into(templateDest)
|
|
||||||
rename { "art/arcane/iris/$it" }
|
|
||||||
expand(inputs.properties)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.generateSentryBundleIdJava {
|
|
||||||
dependsOn(generateTemplates)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.tasks.named("prepareKotlinBuildScriptModel") {
|
|
||||||
dependsOn(generateTemplates)
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets.main {
|
|
||||||
java.srcDir(generateTemplates.map { it.outputs })
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ import art.arcane.iris.engine.object.IrisExternalDatapackReplaceTargets;
|
|||||||
import art.arcane.iris.engine.object.IrisExternalDatapackStructureAlias;
|
import art.arcane.iris.engine.object.IrisExternalDatapackStructureAlias;
|
||||||
import art.arcane.iris.engine.object.IrisExternalDatapackStructureSetAlias;
|
import art.arcane.iris.engine.object.IrisExternalDatapackStructureSetAlias;
|
||||||
import art.arcane.iris.engine.object.IrisExternalDatapackStructurePatch;
|
import art.arcane.iris.engine.object.IrisExternalDatapackStructurePatch;
|
||||||
|
import art.arcane.iris.engine.object.IrisExternalDatapackTemplateAlias;
|
||||||
import art.arcane.iris.engine.object.TileData;
|
import art.arcane.iris.engine.object.TileData;
|
||||||
import art.arcane.iris.util.common.data.B;
|
import art.arcane.iris.util.common.data.B;
|
||||||
import art.arcane.iris.util.common.math.Vector3i;
|
import art.arcane.iris.util.common.math.Vector3i;
|
||||||
@@ -436,6 +437,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
+ ", installedDatapacks=" + projectionResult.installedDatapacks()
|
+ ", installedDatapacks=" + projectionResult.installedDatapacks()
|
||||||
+ ", installedAssets=" + projectionResult.installedAssets()
|
+ ", installedAssets=" + projectionResult.installedAssets()
|
||||||
+ ", syntheticStructureSets=" + projectionResult.syntheticStructureSets()
|
+ ", syntheticStructureSets=" + projectionResult.syntheticStructureSets()
|
||||||
|
+ ", templateAliasesApplied=" + projectionResult.templateAliasesApplied()
|
||||||
|
+ ", emptyElementConversions=" + projectionResult.emptyElementConversions()
|
||||||
|
+ ", unresolvedTemplateRefs=" + projectionResult.unresolvedTemplateRefs()
|
||||||
+ ", success=true");
|
+ ", success=true");
|
||||||
} else {
|
} else {
|
||||||
Iris.warn("External datapack projection: id=" + request.id()
|
Iris.warn("External datapack projection: id=" + request.id()
|
||||||
@@ -445,6 +449,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
+ ", installedDatapacks=" + projectionResult.installedDatapacks()
|
+ ", installedDatapacks=" + projectionResult.installedDatapacks()
|
||||||
+ ", installedAssets=" + projectionResult.installedAssets()
|
+ ", installedAssets=" + projectionResult.installedAssets()
|
||||||
+ ", syntheticStructureSets=" + projectionResult.syntheticStructureSets()
|
+ ", syntheticStructureSets=" + projectionResult.syntheticStructureSets()
|
||||||
|
+ ", templateAliasesApplied=" + projectionResult.templateAliasesApplied()
|
||||||
|
+ ", emptyElementConversions=" + projectionResult.emptyElementConversions()
|
||||||
|
+ ", unresolvedTemplateRefs=" + projectionResult.unresolvedTemplateRefs()
|
||||||
+ ", success=false"
|
+ ", success=false"
|
||||||
+ ", reason=" + projectionResult.error());
|
+ ", reason=" + projectionResult.error());
|
||||||
}
|
}
|
||||||
@@ -1184,7 +1191,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
if (request.required()) {
|
if (request.required()) {
|
||||||
return ProjectionResult.failure(managedName, "no target world datapack folder is available for required external datapack request");
|
return ProjectionResult.failure(managedName, "no target world datapack folder is available for required external datapack request");
|
||||||
}
|
}
|
||||||
return ProjectionResult.success(managedName, 0, 0, Set.copyOf(request.resolvedLocateStructures()), 0, Set.of());
|
return ProjectionResult.success(managedName, 0, 0, Set.copyOf(request.resolvedLocateStructures()), 0, Set.of(), 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionAssetSummary projectionAssetSummary;
|
ProjectionAssetSummary projectionAssetSummary;
|
||||||
@@ -1203,7 +1210,10 @@ public final class ExternalDataPackPipeline {
|
|||||||
0,
|
0,
|
||||||
projectionAssetSummary.resolvedLocateStructures(),
|
projectionAssetSummary.resolvedLocateStructures(),
|
||||||
projectionAssetSummary.syntheticStructureSets(),
|
projectionAssetSummary.syntheticStructureSets(),
|
||||||
projectionAssetSummary.projectedStructureKeys()
|
projectionAssetSummary.projectedStructureKeys(),
|
||||||
|
projectionAssetSummary.templateAliasesApplied(),
|
||||||
|
projectionAssetSummary.emptyElementConversions(),
|
||||||
|
projectionAssetSummary.unresolvedTemplateRefs()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1245,7 +1255,10 @@ public final class ExternalDataPackPipeline {
|
|||||||
installedAssets,
|
installedAssets,
|
||||||
projectionAssetSummary.resolvedLocateStructures(),
|
projectionAssetSummary.resolvedLocateStructures(),
|
||||||
projectionAssetSummary.syntheticStructureSets(),
|
projectionAssetSummary.syntheticStructureSets(),
|
||||||
projectionAssetSummary.projectedStructureKeys()
|
projectionAssetSummary.projectedStructureKeys(),
|
||||||
|
projectionAssetSummary.templateAliasesApplied(),
|
||||||
|
projectionAssetSummary.emptyElementConversions(),
|
||||||
|
projectionAssetSummary.unresolvedTemplateRefs()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1257,7 +1270,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
|
|
||||||
List<ProjectionInputAsset> inputAssets = projectionSelection.assets();
|
List<ProjectionInputAsset> inputAssets = projectionSelection.assets();
|
||||||
if (inputAssets.isEmpty()) {
|
if (inputAssets.isEmpty()) {
|
||||||
return new ProjectionAssetSummary(List.of(), Set.copyOf(request.resolvedLocateStructures()), 0, Set.of());
|
return new ProjectionAssetSummary(List.of(), Set.copyOf(request.resolvedLocateStructures()), 0, Set.of(), 0, 0, 0);
|
||||||
}
|
}
|
||||||
int selectedStructureNbtCount = 0;
|
int selectedStructureNbtCount = 0;
|
||||||
for (ProjectionInputAsset inputAsset : inputAssets) {
|
for (ProjectionInputAsset inputAsset : inputAssets) {
|
||||||
@@ -1290,6 +1303,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
LinkedHashSet<String> remappedStructureKeys = new LinkedHashSet<>();
|
LinkedHashSet<String> remappedStructureKeys = new LinkedHashSet<>();
|
||||||
LinkedHashSet<String> projectedStructureKeys = new LinkedHashSet<>();
|
LinkedHashSet<String> projectedStructureKeys = new LinkedHashSet<>();
|
||||||
LinkedHashSet<String> structureSetReferences = new LinkedHashSet<>();
|
LinkedHashSet<String> structureSetReferences = new LinkedHashSet<>();
|
||||||
|
int templateAliasesApplied = 0;
|
||||||
|
int emptyElementConversions = 0;
|
||||||
|
LinkedHashSet<String> unresolvedTemplateRefs = new LinkedHashSet<>();
|
||||||
LinkedHashSet<String> writtenPaths = new LinkedHashSet<>();
|
LinkedHashSet<String> writtenPaths = new LinkedHashSet<>();
|
||||||
ArrayList<ProjectionOutputAsset> outputAssets = new ArrayList<>();
|
ArrayList<ProjectionOutputAsset> outputAssets = new ArrayList<>();
|
||||||
int projectedCanonicalStructureNbtCount = 0;
|
int projectedCanonicalStructureNbtCount = 0;
|
||||||
@@ -1318,6 +1334,13 @@ public final class ExternalDataPackPipeline {
|
|||||||
JSONObject root = new JSONObject(new String(outputBytes, StandardCharsets.UTF_8));
|
JSONObject root = new JSONObject(new String(outputBytes, StandardCharsets.UTF_8));
|
||||||
rewriteJsonValues(root, remapStringValues);
|
rewriteJsonValues(root, remapStringValues);
|
||||||
|
|
||||||
|
if (projectedEntry.type() == ProjectedEntryType.TEMPLATE_POOL && !request.templateAliases().isEmpty()) {
|
||||||
|
TemplateAliasRewriteResult templateAliasRewriteResult = applyTemplateAliasesToTemplatePool(root, request.templateAliases());
|
||||||
|
templateAliasesApplied += templateAliasRewriteResult.appliedCount();
|
||||||
|
emptyElementConversions += templateAliasRewriteResult.emptyConversions();
|
||||||
|
unresolvedTemplateRefs.addAll(templateAliasRewriteResult.unresolvedReferences());
|
||||||
|
}
|
||||||
|
|
||||||
if (projectedEntry.type() == ProjectedEntryType.STRUCTURE) {
|
if (projectedEntry.type() == ProjectedEntryType.STRUCTURE) {
|
||||||
Integer startHeightAbsolute = getPatchedStartHeightAbsolute(projectedEntry, request);
|
Integer startHeightAbsolute = getPatchedStartHeightAbsolute(projectedEntry, request);
|
||||||
if (startHeightAbsolute == null && remappedKey != null) {
|
if (startHeightAbsolute == null && remappedKey != null) {
|
||||||
@@ -1395,17 +1418,26 @@ public final class ExternalDataPackPipeline {
|
|||||||
throw new IOException("Required external datapack projection produced no canonical structure template outputs (data/*/structure/*.nbt).");
|
throw new IOException("Required external datapack projection produced no canonical structure template outputs (data/*/structure/*.nbt).");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.required() && !unresolvedTemplateRefs.isEmpty()) {
|
||||||
|
throw new IOException("Required external datapack template alias rewrite unresolved reference(s): "
|
||||||
|
+ summarizeMissingSeededTargets(unresolvedTemplateRefs));
|
||||||
|
}
|
||||||
|
|
||||||
if (request.replaceVanilla() && !request.alongsideMode()) {
|
if (request.replaceVanilla() && !request.alongsideMode()) {
|
||||||
int directTargetCount = projectionSelection.directResolvedTargets().size();
|
int directTargetCount = projectionSelection.directResolvedTargets().size();
|
||||||
int aliasTargetCount = projectionSelection.aliasResolvedTargets().size();
|
int aliasTargetCount = projectionSelection.aliasResolvedTargets().size();
|
||||||
int projectedStructureCount = projectedStructureKeys.size();
|
int projectedStructureCount = projectedStructureKeys.size();
|
||||||
int projectedTemplateCount = projectedCanonicalStructureNbtCount;
|
int projectedTemplateCount = projectedCanonicalStructureNbtCount;
|
||||||
|
int unresolvedTemplateRefCount = unresolvedTemplateRefs.size();
|
||||||
if (request.required()) {
|
if (request.required()) {
|
||||||
Iris.info("External datapack strict replace validation: id=" + request.id()
|
Iris.info("External datapack strict replace validation: id=" + request.id()
|
||||||
+ ", directTargets=" + directTargetCount
|
+ ", directTargets=" + directTargetCount
|
||||||
+ ", aliasTargets=" + aliasTargetCount
|
+ ", aliasTargets=" + aliasTargetCount
|
||||||
+ ", projectedStructures=" + projectedStructureCount
|
+ ", projectedStructures=" + projectedStructureCount
|
||||||
+ ", templates=" + projectedTemplateCount
|
+ ", templates=" + projectedTemplateCount
|
||||||
|
+ ", templateAliasesApplied=" + templateAliasesApplied
|
||||||
|
+ ", emptyElementConversions=" + emptyElementConversions
|
||||||
|
+ ", unresolvedTemplateRefs=" + unresolvedTemplateRefCount
|
||||||
+ ", missingTargets=0");
|
+ ", missingTargets=0");
|
||||||
} else {
|
} else {
|
||||||
Iris.verbose("External datapack strict replace validation: id=" + request.id()
|
Iris.verbose("External datapack strict replace validation: id=" + request.id()
|
||||||
@@ -1413,11 +1445,22 @@ public final class ExternalDataPackPipeline {
|
|||||||
+ ", aliasTargets=" + aliasTargetCount
|
+ ", aliasTargets=" + aliasTargetCount
|
||||||
+ ", projectedStructures=" + projectedStructureCount
|
+ ", projectedStructures=" + projectedStructureCount
|
||||||
+ ", templates=" + projectedTemplateCount
|
+ ", templates=" + projectedTemplateCount
|
||||||
|
+ ", templateAliasesApplied=" + templateAliasesApplied
|
||||||
|
+ ", emptyElementConversions=" + emptyElementConversions
|
||||||
|
+ ", unresolvedTemplateRefs=" + unresolvedTemplateRefCount
|
||||||
+ ", missingTargets=0");
|
+ ", missingTargets=0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ProjectionAssetSummary(outputAssets, Set.copyOf(resolvedLocateStructures), syntheticStructureSets, Set.copyOf(projectedStructureKeys));
|
return new ProjectionAssetSummary(
|
||||||
|
outputAssets,
|
||||||
|
Set.copyOf(resolvedLocateStructures),
|
||||||
|
syntheticStructureSets,
|
||||||
|
Set.copyOf(projectedStructureKeys),
|
||||||
|
templateAliasesApplied,
|
||||||
|
emptyElementConversions,
|
||||||
|
unresolvedTemplateRefs.size()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ProjectionSelection readProjectedEntries(File source, DatapackRequest request) throws IOException {
|
private static ProjectionSelection readProjectedEntries(File source, DatapackRequest request) throws IOException {
|
||||||
@@ -2492,6 +2535,75 @@ public final class ExternalDataPackPipeline {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TemplateAliasRewriteResult applyTemplateAliasesToTemplatePool(JSONObject root, Map<String, String> templateAliases) {
|
||||||
|
if (root == null || templateAliases == null || templateAliases.isEmpty()) {
|
||||||
|
return TemplateAliasRewriteResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray elements = root.optJSONArray("elements");
|
||||||
|
if (elements == null || elements.length() <= 0) {
|
||||||
|
return TemplateAliasRewriteResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedHashSet<String> referenced = new LinkedHashSet<>();
|
||||||
|
LinkedHashSet<String> rewritten = new LinkedHashSet<>();
|
||||||
|
LinkedHashSet<String> unresolved = new LinkedHashSet<>();
|
||||||
|
int applied = 0;
|
||||||
|
int emptyConversions = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < elements.length(); i++) {
|
||||||
|
JSONObject poolElement = elements.optJSONObject(i);
|
||||||
|
if (poolElement == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject elementData = poolElement.optJSONObject("element");
|
||||||
|
if (elementData == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String location = elementData.optString("location", "");
|
||||||
|
if (location.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedLocation = normalizeResourceKey("minecraft", location, "structure/", "structures/");
|
||||||
|
if (normalizedLocation == null || normalizedLocation.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String aliasTarget = templateAliases.get(normalizedLocation);
|
||||||
|
if (aliasTarget == null || aliasTarget.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
referenced.add(normalizedLocation);
|
||||||
|
try {
|
||||||
|
if ("minecraft:empty".equalsIgnoreCase(aliasTarget)) {
|
||||||
|
JSONObject emptyElement = new JSONObject();
|
||||||
|
emptyElement.put("element_type", "minecraft:empty_pool_element");
|
||||||
|
poolElement.put("element", emptyElement);
|
||||||
|
emptyConversions++;
|
||||||
|
} else {
|
||||||
|
elementData.put("location", aliasTarget);
|
||||||
|
poolElement.put("element", elementData);
|
||||||
|
}
|
||||||
|
applied++;
|
||||||
|
rewritten.add(normalizedLocation);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
unresolved.add(normalizedLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String reference : referenced) {
|
||||||
|
if (!rewritten.contains(reference)) {
|
||||||
|
unresolved.add(reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TemplateAliasRewriteResult(applied, emptyConversions, unresolved);
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<String> readStructureSetReferences(JSONObject root) {
|
private static Set<String> readStructureSetReferences(JSONObject root) {
|
||||||
LinkedHashSet<String> references = new LinkedHashSet<>();
|
LinkedHashSet<String> references = new LinkedHashSet<>();
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
@@ -4002,6 +4114,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
Set<String> structureSets,
|
Set<String> structureSets,
|
||||||
Map<String, List<String>> structureAliases,
|
Map<String, List<String>> structureAliases,
|
||||||
Map<String, String> structureSetAliases,
|
Map<String, String> structureSetAliases,
|
||||||
|
Map<String, String> templateAliases,
|
||||||
Set<String> configuredFeatures,
|
Set<String> configuredFeatures,
|
||||||
Set<String> placedFeatures,
|
Set<String> placedFeatures,
|
||||||
Set<String> templatePools,
|
Set<String> templatePools,
|
||||||
@@ -4024,6 +4137,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
IrisExternalDatapackReplaceTargets replaceTargets,
|
IrisExternalDatapackReplaceTargets replaceTargets,
|
||||||
KList<IrisExternalDatapackStructureAlias> structureAliases,
|
KList<IrisExternalDatapackStructureAlias> structureAliases,
|
||||||
KList<IrisExternalDatapackStructureSetAlias> structureSetAliases,
|
KList<IrisExternalDatapackStructureSetAlias> structureSetAliases,
|
||||||
|
KList<IrisExternalDatapackTemplateAlias> templateAliases,
|
||||||
KList<IrisExternalDatapackStructurePatch> structurePatches
|
KList<IrisExternalDatapackStructurePatch> structurePatches
|
||||||
) {
|
) {
|
||||||
this(
|
this(
|
||||||
@@ -4037,6 +4151,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
replaceTargets,
|
replaceTargets,
|
||||||
structureAliases,
|
structureAliases,
|
||||||
structureSetAliases,
|
structureSetAliases,
|
||||||
|
templateAliases,
|
||||||
structurePatches,
|
structurePatches,
|
||||||
Set.of(),
|
Set.of(),
|
||||||
"dimension-root",
|
"dimension-root",
|
||||||
@@ -4056,6 +4171,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
IrisExternalDatapackReplaceTargets replaceTargets,
|
IrisExternalDatapackReplaceTargets replaceTargets,
|
||||||
KList<IrisExternalDatapackStructureAlias> structureAliases,
|
KList<IrisExternalDatapackStructureAlias> structureAliases,
|
||||||
KList<IrisExternalDatapackStructureSetAlias> structureSetAliases,
|
KList<IrisExternalDatapackStructureSetAlias> structureSetAliases,
|
||||||
|
KList<IrisExternalDatapackTemplateAlias> templateAliases,
|
||||||
KList<IrisExternalDatapackStructurePatch> structurePatches,
|
KList<IrisExternalDatapackStructurePatch> structurePatches,
|
||||||
Set<String> forcedBiomeKeys,
|
Set<String> forcedBiomeKeys,
|
||||||
String scopeKey,
|
String scopeKey,
|
||||||
@@ -4074,6 +4190,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getStructureSets(), "worldgen/structure_set/"),
|
normalizeTargets(replaceTargets == null ? null : replaceTargets.getStructureSets(), "worldgen/structure_set/"),
|
||||||
normalizeStructureAliases(structureAliases),
|
normalizeStructureAliases(structureAliases),
|
||||||
normalizeStructureSetAliases(structureSetAliases),
|
normalizeStructureSetAliases(structureSetAliases),
|
||||||
|
normalizeTemplateAliases(templateAliases),
|
||||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getConfiguredFeatures(), "worldgen/configured_feature/"),
|
normalizeTargets(replaceTargets == null ? null : replaceTargets.getConfiguredFeatures(), "worldgen/configured_feature/"),
|
||||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getPlacedFeatures(), "worldgen/placed_feature/"),
|
normalizeTargets(replaceTargets == null ? null : replaceTargets.getPlacedFeatures(), "worldgen/placed_feature/"),
|
||||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getTemplatePools(), "worldgen/template_pool/"),
|
normalizeTargets(replaceTargets == null ? null : replaceTargets.getTemplatePools(), "worldgen/template_pool/"),
|
||||||
@@ -4099,6 +4216,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
structureSets = immutableSet(structureSets);
|
structureSets = immutableSet(structureSets);
|
||||||
structureAliases = immutableStructureAliasMap(structureAliases);
|
structureAliases = immutableStructureAliasMap(structureAliases);
|
||||||
structureSetAliases = immutableAliasMap(structureSetAliases);
|
structureSetAliases = immutableAliasMap(structureSetAliases);
|
||||||
|
templateAliases = immutableAliasMap(templateAliases);
|
||||||
configuredFeatures = immutableSet(configuredFeatures);
|
configuredFeatures = immutableSet(configuredFeatures);
|
||||||
placedFeatures = immutableSet(placedFeatures);
|
placedFeatures = immutableSet(placedFeatures);
|
||||||
templatePools = immutableSet(templatePools);
|
templatePools = immutableSet(templatePools);
|
||||||
@@ -4145,6 +4263,7 @@ public final class ExternalDataPackPipeline {
|
|||||||
union(structureSets, other.structureSets),
|
union(structureSets, other.structureSets),
|
||||||
unionStructureAliases(structureAliases, other.structureAliases),
|
unionStructureAliases(structureAliases, other.structureAliases),
|
||||||
unionAliases(structureSetAliases, other.structureSetAliases),
|
unionAliases(structureSetAliases, other.structureSetAliases),
|
||||||
|
unionAliases(templateAliases, other.templateAliases),
|
||||||
union(configuredFeatures, other.configuredFeatures),
|
union(configuredFeatures, other.configuredFeatures),
|
||||||
union(placedFeatures, other.placedFeatures),
|
union(placedFeatures, other.placedFeatures),
|
||||||
union(templatePools, other.templatePools),
|
union(templatePools, other.templatePools),
|
||||||
@@ -4274,6 +4393,40 @@ public final class ExternalDataPackPipeline {
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> normalizeTemplateAliases(KList<IrisExternalDatapackTemplateAlias> aliases) {
|
||||||
|
LinkedHashMap<String, String> normalized = new LinkedHashMap<>();
|
||||||
|
if (aliases == null) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IrisExternalDatapackTemplateAlias alias : aliases) {
|
||||||
|
if (alias == null || !alias.isEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String from = normalizeResourceKey("minecraft", alias.getFrom(), "structure/", "structures/");
|
||||||
|
if (from == null || from.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String toRaw = alias.getTo() == null ? "" : alias.getTo().trim();
|
||||||
|
String to;
|
||||||
|
if ("minecraft:empty".equalsIgnoreCase(toRaw)) {
|
||||||
|
to = "minecraft:empty";
|
||||||
|
} else {
|
||||||
|
to = normalizeResourceKey("minecraft", toRaw, "structure/", "structures/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to == null || to.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized.put(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<String> immutableSet(Set<String> values) {
|
private static Set<String> immutableSet(Set<String> values) {
|
||||||
LinkedHashSet<String> copy = new LinkedHashSet<>();
|
LinkedHashSet<String> copy = new LinkedHashSet<>();
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
@@ -4666,6 +4819,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
Set<String> resolvedLocateStructures,
|
Set<String> resolvedLocateStructures,
|
||||||
int syntheticStructureSets,
|
int syntheticStructureSets,
|
||||||
Set<String> projectedStructureKeys,
|
Set<String> projectedStructureKeys,
|
||||||
|
int templateAliasesApplied,
|
||||||
|
int emptyElementConversions,
|
||||||
|
int unresolvedTemplateRefs,
|
||||||
String managedName,
|
String managedName,
|
||||||
String error
|
String error
|
||||||
) {
|
) {
|
||||||
@@ -4691,6 +4847,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
}
|
}
|
||||||
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
||||||
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
||||||
|
templateAliasesApplied = Math.max(0, templateAliasesApplied);
|
||||||
|
emptyElementConversions = Math.max(0, emptyElementConversions);
|
||||||
|
unresolvedTemplateRefs = Math.max(0, unresolvedTemplateRefs);
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
error = "";
|
error = "";
|
||||||
}
|
}
|
||||||
@@ -4702,14 +4861,29 @@ public final class ExternalDataPackPipeline {
|
|||||||
int installedAssets,
|
int installedAssets,
|
||||||
Set<String> resolvedLocateStructures,
|
Set<String> resolvedLocateStructures,
|
||||||
int syntheticStructureSets,
|
int syntheticStructureSets,
|
||||||
Set<String> projectedStructureKeys
|
Set<String> projectedStructureKeys,
|
||||||
|
int templateAliasesApplied,
|
||||||
|
int emptyElementConversions,
|
||||||
|
int unresolvedTemplateRefs
|
||||||
) {
|
) {
|
||||||
return new ProjectionResult(true, installedDatapacks, installedAssets, resolvedLocateStructures, syntheticStructureSets, projectedStructureKeys, managedName, "");
|
return new ProjectionResult(
|
||||||
|
true,
|
||||||
|
installedDatapacks,
|
||||||
|
installedAssets,
|
||||||
|
resolvedLocateStructures,
|
||||||
|
syntheticStructureSets,
|
||||||
|
projectedStructureKeys,
|
||||||
|
templateAliasesApplied,
|
||||||
|
emptyElementConversions,
|
||||||
|
unresolvedTemplateRefs,
|
||||||
|
managedName,
|
||||||
|
""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ProjectionResult failure(String managedName, String error) {
|
private static ProjectionResult failure(String managedName, String error) {
|
||||||
String message = error == null || error.isBlank() ? "projection failed" : error;
|
String message = error == null || error.isBlank() ? "projection failed" : error;
|
||||||
return new ProjectionResult(false, 0, 0, Set.of(), 0, Set.of(), managedName, message);
|
return new ProjectionResult(false, 0, 0, Set.of(), 0, Set.of(), 0, 0, 0, managedName, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4725,11 +4899,30 @@ public final class ExternalDataPackPipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record TemplateAliasRewriteResult(
|
||||||
|
int appliedCount,
|
||||||
|
int emptyConversions,
|
||||||
|
Set<String> unresolvedReferences
|
||||||
|
) {
|
||||||
|
private TemplateAliasRewriteResult {
|
||||||
|
appliedCount = Math.max(0, appliedCount);
|
||||||
|
emptyConversions = Math.max(0, emptyConversions);
|
||||||
|
unresolvedReferences = unresolvedReferences == null ? Set.of() : Set.copyOf(unresolvedReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TemplateAliasRewriteResult empty() {
|
||||||
|
return new TemplateAliasRewriteResult(0, 0, Set.of());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private record ProjectionAssetSummary(
|
private record ProjectionAssetSummary(
|
||||||
List<ProjectionOutputAsset> assets,
|
List<ProjectionOutputAsset> assets,
|
||||||
Set<String> resolvedLocateStructures,
|
Set<String> resolvedLocateStructures,
|
||||||
int syntheticStructureSets,
|
int syntheticStructureSets,
|
||||||
Set<String> projectedStructureKeys
|
Set<String> projectedStructureKeys,
|
||||||
|
int templateAliasesApplied,
|
||||||
|
int emptyElementConversions,
|
||||||
|
int unresolvedTemplateRefs
|
||||||
) {
|
) {
|
||||||
private ProjectionAssetSummary {
|
private ProjectionAssetSummary {
|
||||||
assets = assets == null ? List.of() : List.copyOf(assets);
|
assets = assets == null ? List.of() : List.copyOf(assets);
|
||||||
@@ -4754,6 +4947,9 @@ public final class ExternalDataPackPipeline {
|
|||||||
}
|
}
|
||||||
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
||||||
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
||||||
|
templateAliasesApplied = Math.max(0, templateAliasesApplied);
|
||||||
|
emptyElementConversions = Math.max(0, emptyElementConversions);
|
||||||
|
unresolvedTemplateRefs = Math.max(0, unresolvedTemplateRefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,6 @@ public class IrisSettings {
|
|||||||
public int noiseCacheSize = 1_024;
|
public int noiseCacheSize = 1_024;
|
||||||
public int resourceLoaderCacheSize = 1_024;
|
public int resourceLoaderCacheSize = 1_024;
|
||||||
public int objectLoaderCacheSize = 4_096;
|
public int objectLoaderCacheSize = 4_096;
|
||||||
public int scriptLoaderCacheSize = 512;
|
|
||||||
public int tectonicPlateSize = -1;
|
public int tectonicPlateSize = -1;
|
||||||
public int mantleCleanupDelay = 200;
|
public int mantleCleanupDelay = 200;
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ public class ServerConfigurator {
|
|||||||
definition.getReplaceTargets(),
|
definition.getReplaceTargets(),
|
||||||
definition.getStructureAliases(),
|
definition.getStructureAliases(),
|
||||||
definition.getStructureSetAliases(),
|
definition.getStructureSetAliases(),
|
||||||
|
definition.getTemplateAliases(),
|
||||||
definition.getStructurePatches(),
|
definition.getStructurePatches(),
|
||||||
Set.of(),
|
Set.of(),
|
||||||
scopeKey,
|
scopeKey,
|
||||||
@@ -374,6 +375,7 @@ public class ServerConfigurator {
|
|||||||
definition.getReplaceTargets(),
|
definition.getReplaceTargets(),
|
||||||
definition.getStructureAliases(),
|
definition.getStructureAliases(),
|
||||||
definition.getStructureSetAliases(),
|
definition.getStructureSetAliases(),
|
||||||
|
definition.getTemplateAliases(),
|
||||||
definition.getStructurePatches(),
|
definition.getStructurePatches(),
|
||||||
group.forcedBiomeKeys(),
|
group.forcedBiomeKeys(),
|
||||||
group.scopeKey(),
|
group.scopeKey(),
|
||||||
|
|||||||
@@ -277,14 +277,6 @@ public class CommandStudio implements DirectorExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Director(description = "Execute a script", aliases = "run", origin = DirectorOrigin.PLAYER)
|
|
||||||
public void execute(
|
|
||||||
@Param(description = "The script to run")
|
|
||||||
IrisScript script
|
|
||||||
) {
|
|
||||||
engine().getExecution().execute(script.getLoadKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Director(description = "Open the noise explorer (External GUI)", aliases = {"nmap", "n"})
|
@Director(description = "Open the noise explorer (External GUI)", aliases = {"nmap", "n"})
|
||||||
public void noise() {
|
public void noise() {
|
||||||
if (noGUI()) return;
|
if (noGUI()) return;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.google.gson.stream.JsonReader;
|
|||||||
import com.google.gson.stream.JsonToken;
|
import com.google.gson.stream.JsonToken;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import art.arcane.iris.Iris;
|
import art.arcane.iris.Iris;
|
||||||
import art.arcane.iris.core.scripting.environment.PackEnvironment;
|
|
||||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||||
import art.arcane.iris.engine.framework.Engine;
|
import art.arcane.iris.engine.framework.Engine;
|
||||||
import art.arcane.iris.engine.object.*;
|
import art.arcane.iris.engine.object.*;
|
||||||
@@ -32,8 +31,6 @@ import art.arcane.iris.engine.object.annotations.Snippet;
|
|||||||
import art.arcane.iris.engine.object.matter.IrisMatterObject;
|
import art.arcane.iris.engine.object.matter.IrisMatterObject;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.collection.KMap;
|
import art.arcane.volmlib.util.collection.KMap;
|
||||||
import art.arcane.iris.util.project.context.IrisContext;
|
|
||||||
import art.arcane.iris.util.common.format.C;
|
|
||||||
import art.arcane.volmlib.util.mantle.flag.MantleFlagAdapter;
|
import art.arcane.volmlib.util.mantle.flag.MantleFlagAdapter;
|
||||||
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
||||||
import art.arcane.volmlib.util.math.RNG;
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
@@ -58,7 +55,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
private final File dataFolder;
|
private final File dataFolder;
|
||||||
private final int id;
|
private final int id;
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
private PackEnvironment environment;
|
|
||||||
private ResourceLoader<IrisBiome> biomeLoader;
|
private ResourceLoader<IrisBiome> biomeLoader;
|
||||||
private ResourceLoader<IrisLootTable> lootLoader;
|
private ResourceLoader<IrisLootTable> lootLoader;
|
||||||
private ResourceLoader<IrisRegion> regionLoader;
|
private ResourceLoader<IrisRegion> regionLoader;
|
||||||
@@ -73,7 +69,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
private ResourceLoader<IrisObject> objectLoader;
|
private ResourceLoader<IrisObject> objectLoader;
|
||||||
private ResourceLoader<IrisMatterObject> matterLoader;
|
private ResourceLoader<IrisMatterObject> matterLoader;
|
||||||
private ResourceLoader<IrisImage> imageLoader;
|
private ResourceLoader<IrisImage> imageLoader;
|
||||||
private ResourceLoader<IrisScript> scriptLoader;
|
|
||||||
private ResourceLoader<IrisMatterObject> matterObjectLoader;
|
private ResourceLoader<IrisMatterObject> matterObjectLoader;
|
||||||
private KMap<String, KList<String>> possibleSnippets;
|
private KMap<String, KList<String>> possibleSnippets;
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
@@ -152,10 +147,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
return loadAny(IrisSpawner.class, key, nearest);
|
return loadAny(IrisSpawner.class, key, nearest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IrisScript loadAnyScript(String key, @Nullable IrisData nearest) {
|
|
||||||
return loadAny(IrisScript.class, key, nearest);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IrisRegion loadAnyRegion(String key, @Nullable IrisData nearest) {
|
public static IrisRegion loadAnyRegion(String key, @Nullable IrisData nearest) {
|
||||||
return loadAny(IrisRegion.class, key, nearest);
|
return loadAny(IrisRegion.class, key, nearest);
|
||||||
}
|
}
|
||||||
@@ -237,45 +228,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void preprocessObject(IrisRegistrant t) {
|
public void preprocessObject(IrisRegistrant t) {
|
||||||
try {
|
|
||||||
IrisContext ctx = IrisContext.get();
|
|
||||||
Engine engine = this.engine;
|
|
||||||
|
|
||||||
if (engine == null && ctx != null && ctx.getEngine() != null) {
|
|
||||||
engine = ctx.getEngine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine == null && t.getPreprocessors().isNotEmpty()) {
|
|
||||||
Iris.error("Failed to preprocess object " + t.getLoadKey() + " because there is no engine context here. (See stack below)");
|
|
||||||
try {
|
|
||||||
throw new RuntimeException();
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine == null) return;
|
|
||||||
var global = engine.getDimension().getPreProcessors(t.getFolderName());
|
|
||||||
var local = t.getPreprocessors();
|
|
||||||
if ((global != null && global.isNotEmpty()) || local.isNotEmpty()) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (global != null) {
|
|
||||||
for (String i : global) {
|
|
||||||
engine.getExecution().preprocessObject(i, t);
|
|
||||||
Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in <rainbow>" + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String i : local) {
|
|
||||||
engine.getExecution().preprocessObject(i, t);
|
|
||||||
Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in <rainbow>" + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("Failed to preprocess object!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
@@ -298,9 +250,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
} else if (registrant.equals(IrisMatterObject.class)) {
|
} else if (registrant.equals(IrisMatterObject.class)) {
|
||||||
r = (ResourceLoader<T>) new MatterObjectResourceLoader(dataFolder, this, rr.getFolderName(),
|
r = (ResourceLoader<T>) new MatterObjectResourceLoader(dataFolder, this, rr.getFolderName(),
|
||||||
rr.getTypeName());
|
rr.getTypeName());
|
||||||
} else if (registrant.equals(IrisScript.class)) {
|
|
||||||
r = (ResourceLoader<T>) new ScriptResourceLoader(dataFolder, this, rr.getFolderName(),
|
|
||||||
rr.getTypeName());
|
|
||||||
} else if (registrant.equals(IrisImage.class)) {
|
} else if (registrant.equals(IrisImage.class)) {
|
||||||
r = (ResourceLoader<T>) new ImageResourceLoader(dataFolder, this, rr.getFolderName(),
|
r = (ResourceLoader<T>) new ImageResourceLoader(dataFolder, this, rr.getFolderName(),
|
||||||
rr.getTypeName());
|
rr.getTypeName());
|
||||||
@@ -347,16 +296,10 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
this.expressionLoader = registerLoader(IrisExpression.class);
|
this.expressionLoader = registerLoader(IrisExpression.class);
|
||||||
this.objectLoader = registerLoader(IrisObject.class);
|
this.objectLoader = registerLoader(IrisObject.class);
|
||||||
this.imageLoader = registerLoader(IrisImage.class);
|
this.imageLoader = registerLoader(IrisImage.class);
|
||||||
this.scriptLoader = registerLoader(IrisScript.class);
|
|
||||||
this.matterObjectLoader = registerLoader(IrisMatterObject.class);
|
this.matterObjectLoader = registerLoader(IrisMatterObject.class);
|
||||||
this.environment = PackEnvironment.create(this);
|
|
||||||
builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
|
builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
|
||||||
|
|
||||||
gson = builder.create();
|
gson = builder.create();
|
||||||
dimensionLoader.streamAll()
|
|
||||||
.map(IrisDimension::getDataScripts)
|
|
||||||
.flatMap(KList::stream)
|
|
||||||
.forEach(environment::execute);
|
|
||||||
|
|
||||||
if (engine != null) {
|
if (engine != null) {
|
||||||
engine.hotload();
|
engine.hotload();
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ package art.arcane.iris.core.loader;
|
|||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import art.arcane.iris.Iris;
|
import art.arcane.iris.Iris;
|
||||||
import art.arcane.iris.engine.object.IrisScript;
|
|
||||||
import art.arcane.iris.engine.object.annotations.ArrayType;
|
|
||||||
import art.arcane.iris.engine.object.annotations.Desc;
|
|
||||||
import art.arcane.iris.engine.object.annotations.RegistryListResource;
|
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
import art.arcane.volmlib.util.json.JSONObject;
|
||||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -35,11 +30,6 @@ import java.io.File;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public abstract class IrisRegistrant {
|
public abstract class IrisRegistrant {
|
||||||
@Desc("Preprocess this object in-memory when it's loaded, run scripts using the variable 'object' and modify properties about this object before it's used.\nFile extension: .proc.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@ArrayType(min = 1, type = String.class)
|
|
||||||
private KList<String> preprocessors = new KList<>();
|
|
||||||
|
|
||||||
@EqualsAndHashCode.Exclude
|
@EqualsAndHashCode.Exclude
|
||||||
private transient IrisData loader;
|
private transient IrisData loader;
|
||||||
|
|
||||||
|
|||||||
@@ -1,170 +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 art.arcane.iris.core.loader;
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris;
|
|
||||||
import art.arcane.iris.core.IrisSettings;
|
|
||||||
import art.arcane.iris.engine.object.IrisScript;
|
|
||||||
import art.arcane.volmlib.util.data.KCache;
|
|
||||||
import art.arcane.volmlib.util.io.IO;
|
|
||||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
|
|
||||||
public ScriptResourceLoader(File root, IrisData idm, String folderName, String resourceTypeName) {
|
|
||||||
super(root, idm, folderName, resourceTypeName, IrisScript.class);
|
|
||||||
loadCache = new KCache<>(this::loadRaw, IrisSettings.get().getPerformance().getScriptLoaderCacheSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean supportsSchemas() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return loadCache.getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IrisScript loadFile(File j, String name) {
|
|
||||||
try {
|
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
|
||||||
IrisScript t = new IrisScript(IO.readAll(j));
|
|
||||||
t.setLoadKey(name);
|
|
||||||
t.setLoader(manager);
|
|
||||||
t.setLoadFile(j);
|
|
||||||
logLoad(j, t);
|
|
||||||
tlt.addAndGet(p.getMilliseconds());
|
|
||||||
return t;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + ": " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String[] getPossibleKeys() {
|
|
||||||
if (possibleKeys != null) {
|
|
||||||
return possibleKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.debug("Building " + resourceTypeName + " Possibility Lists");
|
|
||||||
Set<String> keys = new HashSet<>();
|
|
||||||
|
|
||||||
for (File i : getFolders()) {
|
|
||||||
if (i.isDirectory()) {
|
|
||||||
keys.addAll(getKeysInDirectory(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
possibleKeys = keys.toArray(new String[0]);
|
|
||||||
return possibleKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getKeysInDirectory(File directory) {
|
|
||||||
Set<String> keys = new HashSet<>();
|
|
||||||
for (File file : directory.listFiles()) {
|
|
||||||
if (file.isFile() && file.getName().endsWith(".kts")) {
|
|
||||||
keys.add(file.getName().replaceAll("\\Q.kts\\E", ""));
|
|
||||||
} else if (file.isDirectory()) {
|
|
||||||
keys.addAll(getKeysInDirectory(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public String[] getPossibleKeys() {
|
|
||||||
// if (possibleKeys != null) {
|
|
||||||
// return possibleKeys;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Iris.debug("Building " + resourceTypeName + " Possibility Lists");
|
|
||||||
// KSet<String> m = new KSet<>();
|
|
||||||
//
|
|
||||||
// for (File i : getFolders()) {
|
|
||||||
// for (File j : i.listFiles()) {
|
|
||||||
// if (j.isFile() && j.getName().endsWith(".js")) {
|
|
||||||
// m.add(j.getName().replaceAll("\\Q.js\\E", ""));
|
|
||||||
// } else if (j.isDirectory()) {
|
|
||||||
// for (File k : j.listFiles()) {
|
|
||||||
// if (k.isFile() && k.getName().endsWith(".js")) {
|
|
||||||
// m.add(j.getName() + "/" + k.getName().replaceAll("\\Q.js\\E", ""));
|
|
||||||
// } else if (k.isDirectory()) {
|
|
||||||
// for (File l : k.listFiles()) {
|
|
||||||
// if (l.isFile() && l.getName().endsWith(".js")) {
|
|
||||||
// m.add(j.getName() + "/" + k.getName() + "/" + l.getName().replaceAll("\\Q.js\\E", ""));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// KList<String> v = new KList<>(m);
|
|
||||||
// possibleKeys = v.toArray(new String[0]);
|
|
||||||
// return possibleKeys;
|
|
||||||
// }
|
|
||||||
|
|
||||||
public File findFile(String name) {
|
|
||||||
for (File i : getFolders(name)) {
|
|
||||||
for (File j : i.listFiles()) {
|
|
||||||
if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) {
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = new File(i, name + ".kts");
|
|
||||||
|
|
||||||
if (file.exists()) {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.warn("Couldn't find " + resourceTypeName + ": " + name);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IrisScript loadRaw(String name) {
|
|
||||||
for (File i : getFolders(name)) {
|
|
||||||
for (File j : i.listFiles()) {
|
|
||||||
if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) {
|
|
||||||
return loadFile(j, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = new File(i, name + ".kts");
|
|
||||||
|
|
||||||
if (file.exists()) {
|
|
||||||
return loadFile(file, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.warn("Couldn't find " + resourceTypeName + ": " + name);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IrisScript load(String name, boolean warn) {
|
|
||||||
return loadCache.get(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+369
@@ -0,0 +1,369 @@
|
|||||||
|
package art.arcane.iris.core.pregenerator.cache;
|
||||||
|
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.volmlib.util.data.Varint;
|
||||||
|
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||||
|
import art.arcane.volmlib.util.documentation.RegionCoordinates;
|
||||||
|
import art.arcane.volmlib.util.io.IO;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
|
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class PregenCacheImpl implements PregenCache {
|
||||||
|
private static final ExecutorService DISPATCHER = Executors.newFixedThreadPool(4);
|
||||||
|
private static final short SIZE = 1024;
|
||||||
|
|
||||||
|
private final File directory;
|
||||||
|
private final int maxSize;
|
||||||
|
private final Long2ObjectLinkedOpenHashMap<Plate> cache = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
|
|
||||||
|
public PregenCacheImpl(File directory, int maxSize) {
|
||||||
|
this.directory = directory;
|
||||||
|
this.maxSize = maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ChunkCoordinates
|
||||||
|
public boolean isChunkCached(int x, int z) {
|
||||||
|
return getPlate(x >> 10, z >> 10).isCached(
|
||||||
|
(x >> 5) & 31,
|
||||||
|
(z >> 5) & 31,
|
||||||
|
region -> region.isCached(x & 31, z & 31)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RegionCoordinates
|
||||||
|
public boolean isRegionCached(int x, int z) {
|
||||||
|
return getPlate(x >> 5, z >> 5).isCached(
|
||||||
|
x & 31,
|
||||||
|
z & 31,
|
||||||
|
Region::isCached
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void cacheChunk(int x, int z) {
|
||||||
|
getPlate(x >> 10, z >> 10).cache(
|
||||||
|
(x >> 5) & 31,
|
||||||
|
(z >> 5) & 31,
|
||||||
|
region -> region.cache(x & 31, z & 31)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RegionCoordinates
|
||||||
|
public void cacheRegion(int x, int z) {
|
||||||
|
getPlate(x >> 5, z >> 5).cache(
|
||||||
|
x & 31,
|
||||||
|
z & 31,
|
||||||
|
Region::cache
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write() {
|
||||||
|
if (cache.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CompletableFuture<Void>> futures = new ArrayList<>(cache.size());
|
||||||
|
for (Plate plate : cache.values()) {
|
||||||
|
if (!plate.dirty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
futures.add(CompletableFuture.runAsync(() -> writePlate(plate), DISPATCHER));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CompletableFuture<Void> future : futures) {
|
||||||
|
future.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trim(long unloadDuration) {
|
||||||
|
if (cache.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long threshold = System.currentTimeMillis() - unloadDuration;
|
||||||
|
List<CompletableFuture<Void>> futures = new ArrayList<>(cache.size());
|
||||||
|
Iterator<Plate> iterator = cache.values().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Plate plate = iterator.next();
|
||||||
|
if (plate.lastAccess < threshold) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
futures.add(CompletableFuture.runAsync(() -> writePlate(plate), DISPATCHER));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CompletableFuture<Void> future : futures) {
|
||||||
|
future.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plate getPlate(int x, int z) {
|
||||||
|
long key = key(x, z);
|
||||||
|
Plate plate = cache.getAndMoveToFirst(key);
|
||||||
|
if (plate != null) {
|
||||||
|
return plate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plate loaded = readPlate(x, z);
|
||||||
|
cache.putAndMoveToFirst(key, loaded);
|
||||||
|
|
||||||
|
if (cache.size() > maxSize) {
|
||||||
|
List<CompletableFuture<Void>> futures = new ArrayList<>(cache.size() - maxSize);
|
||||||
|
while (cache.size() > maxSize) {
|
||||||
|
Plate evicted = cache.removeLast();
|
||||||
|
futures.add(CompletableFuture.runAsync(() -> writePlate(evicted), DISPATCHER));
|
||||||
|
}
|
||||||
|
for (CompletableFuture<Void> future : futures) {
|
||||||
|
future.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plate readPlate(int x, int z) {
|
||||||
|
File file = fileForPlate(x, z);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new Plate(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DataInputStream input = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) {
|
||||||
|
return readPlate(x, z, input);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.error("Failed to read pregen cache " + file);
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Plate(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writePlate(Plate plate) {
|
||||||
|
if (!plate.dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = fileForPlate(plate.x, plate.z);
|
||||||
|
try {
|
||||||
|
IO.write(file, output -> new DataOutputStream(new LZ4BlockOutputStream(output)), plate::write);
|
||||||
|
plate.dirty = false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.error("Failed to write preen cache " + file);
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File fileForPlate(int x, int z) {
|
||||||
|
if (!directory.exists() && !directory.mkdirs()) {
|
||||||
|
throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath());
|
||||||
|
}
|
||||||
|
return new File(directory, "c." + x + "." + z + ".lz4b");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long key(int x, int z) {
|
||||||
|
return (((long) x) << 32) ^ (z & 0xffffffffL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface RegionPredicate {
|
||||||
|
boolean test(Region region);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Plate {
|
||||||
|
private final int x;
|
||||||
|
private final int z;
|
||||||
|
private short count;
|
||||||
|
private Region[] regions;
|
||||||
|
private boolean dirty;
|
||||||
|
private long lastAccess;
|
||||||
|
|
||||||
|
private Plate(int x, int z) {
|
||||||
|
this(x, z, (short) 0, new Region[1024]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plate(int x, int z, short count, Region[] regions) {
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
this.count = count;
|
||||||
|
this.regions = regions;
|
||||||
|
this.lastAccess = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cache(int x, int z, RegionPredicate predicate) {
|
||||||
|
lastAccess = System.currentTimeMillis();
|
||||||
|
if (count == SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = x * 32 + z;
|
||||||
|
Region region = regions[index];
|
||||||
|
if (region == null) {
|
||||||
|
region = new Region();
|
||||||
|
regions[index] = region;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!predicate.test(region)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count == SIZE) {
|
||||||
|
regions = null;
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCached(int x, int z, RegionPredicate predicate) {
|
||||||
|
lastAccess = System.currentTimeMillis();
|
||||||
|
if (count == SIZE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Region region = regions[x * 32 + z];
|
||||||
|
if (region == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return predicate.test(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(DataOutput output) throws IOException {
|
||||||
|
Varint.writeSignedVarInt(count, output);
|
||||||
|
if (regions == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Region region : regions) {
|
||||||
|
output.writeBoolean(region == null);
|
||||||
|
if (region != null) {
|
||||||
|
region.write(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Region {
|
||||||
|
private short count;
|
||||||
|
private long[] words;
|
||||||
|
|
||||||
|
private Region() {
|
||||||
|
this((short) 0, new long[64]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Region(short count, long[] words) {
|
||||||
|
this.count = count;
|
||||||
|
this.words = words;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cache() {
|
||||||
|
if (count == SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
count = SIZE;
|
||||||
|
words = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cache(int x, int z) {
|
||||||
|
if (count == SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long[] value = words;
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = x * 32 + z;
|
||||||
|
int wordIndex = index >> 6;
|
||||||
|
long bit = 1L << (index & 63);
|
||||||
|
boolean current = (value[wordIndex] & bit) != 0L;
|
||||||
|
if (current) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count == SIZE) {
|
||||||
|
words = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value[wordIndex] = value[wordIndex] | bit;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCached() {
|
||||||
|
return count == SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCached(int x, int z) {
|
||||||
|
int index = x * 32 + z;
|
||||||
|
if (count == SIZE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (words[index >> 6] & (1L << (index & 63))) != 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(DataOutput output) throws IOException {
|
||||||
|
Varint.writeSignedVarInt(count, output);
|
||||||
|
if (words == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long word : words) {
|
||||||
|
Varint.writeUnsignedVarLong(word, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Plate readPlate(int x, int z, DataInput input) throws IOException {
|
||||||
|
int count = Varint.readSignedVarInt(input);
|
||||||
|
if (count == 1024) {
|
||||||
|
return new Plate(x, z, SIZE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Region[] regions = new Region[1024];
|
||||||
|
for (int i = 0; i < regions.length; i++) {
|
||||||
|
boolean isNull = input.readBoolean();
|
||||||
|
if (!isNull) {
|
||||||
|
regions[i] = readRegion(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Plate(x, z, (short) count, regions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Region readRegion(DataInput input) throws IOException {
|
||||||
|
int count = Varint.readSignedVarInt(input);
|
||||||
|
if (count == 1024) {
|
||||||
|
return new Region(SIZE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
long[] words = new long[64];
|
||||||
|
for (int i = 0; i < words.length; i++) {
|
||||||
|
words[i] = Varint.readUnsignedVarLong(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Region((short) count, words);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ public class Gradle {
|
|||||||
|
|
||||||
public static synchronized void wrapper(File projectDir) {
|
public static synchronized void wrapper(File projectDir) {
|
||||||
try {
|
try {
|
||||||
File settings = new File(projectDir, "settings.gradle.kts");
|
File settings = new File(projectDir, "settings.gradle");
|
||||||
if (!settings.exists()) settings.createNewFile();
|
if (!settings.exists()) settings.createNewFile();
|
||||||
runGradle(projectDir, "wrapper");
|
runGradle(projectDir, "wrapper");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
@@ -428,7 +428,6 @@ public class IrisProject {
|
|||||||
settings.put("json.schemas", schemas);
|
settings.put("json.schemas", schemas);
|
||||||
ws.put("settings", settings);
|
ws.put("settings", settings);
|
||||||
|
|
||||||
dm.getEnvironment().configureProject();
|
|
||||||
File schemasFile = new File(path, ".idea" + File.separator + "jsonSchemas.xml");
|
File schemasFile = new File(path, ".idea" + File.separator + "jsonSchemas.xml");
|
||||||
Document doc = IO.read(schemasFile);
|
Document doc = IO.read(schemasFile);
|
||||||
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");
|
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package art.arcane.iris.core.safeguard;
|
||||||
|
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.iris.core.safeguard.task.Diagnostic;
|
||||||
|
import art.arcane.iris.core.safeguard.task.Task;
|
||||||
|
import art.arcane.iris.core.safeguard.task.Tasks;
|
||||||
|
import art.arcane.iris.core.safeguard.task.ValueWithDiagnostics;
|
||||||
|
import art.arcane.iris.util.common.format.C;
|
||||||
|
import art.arcane.iris.util.common.scheduling.J;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class IrisSafeguard {
|
||||||
|
private static volatile boolean forceShutdown = false;
|
||||||
|
private static Map<Task, ValueWithDiagnostics<Mode>> results = Collections.emptyMap();
|
||||||
|
private static Map<String, String> context = Collections.emptyMap();
|
||||||
|
private static Map<String, List<String>> attachment = Collections.emptyMap();
|
||||||
|
private static Mode mode = Mode.STABLE;
|
||||||
|
private static int count = 0;
|
||||||
|
|
||||||
|
private IrisSafeguard() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void execute() {
|
||||||
|
List<Task> tasks = Tasks.getTasks();
|
||||||
|
LinkedHashMap<Task, ValueWithDiagnostics<Mode>> resultValues = new LinkedHashMap<>(tasks.size());
|
||||||
|
LinkedHashMap<String, String> contextValues = new LinkedHashMap<>(tasks.size());
|
||||||
|
LinkedHashMap<String, List<String>> attachmentValues = new LinkedHashMap<>(tasks.size());
|
||||||
|
Mode currentMode = Mode.STABLE;
|
||||||
|
int issueCount = 0;
|
||||||
|
|
||||||
|
for (Task task : tasks) {
|
||||||
|
ValueWithDiagnostics<Mode> result;
|
||||||
|
try {
|
||||||
|
result = task.run();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
result = new ValueWithDiagnostics<>(
|
||||||
|
Mode.WARNING,
|
||||||
|
new Diagnostic(Diagnostic.Logger.ERROR, "Error while running task " + task.getId(), e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMode = currentMode.highest(result.getValue());
|
||||||
|
resultValues.put(task, result);
|
||||||
|
contextValues.put(task.getId(), result.getValue().getId());
|
||||||
|
|
||||||
|
List<String> lines = new ArrayList<>();
|
||||||
|
for (Diagnostic diagnostic : result.getDiagnostics()) {
|
||||||
|
String[] split = diagnostic.toString().split("\\n");
|
||||||
|
Collections.addAll(lines, split);
|
||||||
|
}
|
||||||
|
attachmentValues.put(task.getId(), lines);
|
||||||
|
|
||||||
|
if (result.getValue() != Mode.STABLE) {
|
||||||
|
issueCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results = Collections.unmodifiableMap(resultValues);
|
||||||
|
context = Collections.unmodifiableMap(contextValues);
|
||||||
|
attachment = Collections.unmodifiableMap(attachmentValues);
|
||||||
|
mode = currentMode;
|
||||||
|
count = issueCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mode mode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> asContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, List<String>> asAttachment() {
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void splash() {
|
||||||
|
Iris.instance.splash();
|
||||||
|
printReports();
|
||||||
|
printFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printReports() {
|
||||||
|
switch (mode) {
|
||||||
|
case STABLE -> Iris.info(C.BLUE + "0 Conflicts found");
|
||||||
|
case WARNING -> Iris.warn(C.GOLD + "%s Issues found", count);
|
||||||
|
case UNSTABLE -> Iris.error(C.DARK_RED + "%s Issues found", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ValueWithDiagnostics<Mode> value : results.values()) {
|
||||||
|
value.log(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printFooter() {
|
||||||
|
switch (mode) {
|
||||||
|
case STABLE -> Iris.info(C.BLUE + "Iris is running Stable");
|
||||||
|
case WARNING -> warning();
|
||||||
|
case UNSTABLE -> unstable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isForceShutdown() {
|
||||||
|
return forceShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void warning() {
|
||||||
|
Iris.warn(C.GOLD + "Iris is running in Warning Mode");
|
||||||
|
Iris.warn(C.GRAY + "Some startup checks need attention. Review the messages above for tuning suggestions.");
|
||||||
|
Iris.warn(C.GRAY + "Iris will continue startup normally.");
|
||||||
|
Iris.warn("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unstable() {
|
||||||
|
Iris.error(C.DARK_RED + "Iris is running in Danger Mode");
|
||||||
|
Iris.error("");
|
||||||
|
Iris.error(C.DARK_GRAY + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--");
|
||||||
|
Iris.error("Critical startup checks failed. Iris will continue startup in 10 seconds.");
|
||||||
|
Iris.error("Review and resolve the errors above as soon as possible.");
|
||||||
|
J.sleep(10000L);
|
||||||
|
Iris.info("");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package art.arcane.iris.core.safeguard;
|
||||||
|
|
||||||
|
import art.arcane.iris.BuildConstants;
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.iris.core.IrisSettings;
|
||||||
|
import art.arcane.iris.util.common.format.C;
|
||||||
|
import art.arcane.volmlib.util.format.Form;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum Mode {
|
||||||
|
STABLE(C.IRIS),
|
||||||
|
WARNING(C.GOLD),
|
||||||
|
UNSTABLE(C.RED);
|
||||||
|
|
||||||
|
private final C color;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
Mode(C color) {
|
||||||
|
this.color = color;
|
||||||
|
this.id = name().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mode highest(Mode mode) {
|
||||||
|
if (mode.ordinal() > ordinal()) {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tag(String subTag) {
|
||||||
|
if (subTag == null || subTag.isBlank()) {
|
||||||
|
return wrap("Iris") + C.GRAY + ": ";
|
||||||
|
}
|
||||||
|
return wrap("Iris") + " " + wrap(subTag) + C.GRAY + ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void trySplash() {
|
||||||
|
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
splash();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void splash() {
|
||||||
|
String padd = Form.repeat(" ", 8);
|
||||||
|
String padd2 = Form.repeat(" ", 4);
|
||||||
|
String version = Iris.instance.getDescription().getVersion();
|
||||||
|
String releaseTrain = getReleaseTrain(version);
|
||||||
|
String serverVersion = getServerVersion();
|
||||||
|
String startupDate = getStartupDate();
|
||||||
|
int javaVersion = getJavaVersion();
|
||||||
|
|
||||||
|
String[] splash = new String[]{
|
||||||
|
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
||||||
|
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + color + " .(((()))). ",
|
||||||
|
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + color + " .((((((())))))). ",
|
||||||
|
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + color + " ((((((((())))))))) " + C.GRAY + " @",
|
||||||
|
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + color + " ((((((((-))))))))) " + C.GRAY + " @@",
|
||||||
|
padd + C.GRAY + "@@@&&" + color + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
||||||
|
padd + C.GRAY + "@@" + color + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
||||||
|
padd + C.GRAY + "@" + color + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
||||||
|
padd + C.GRAY + "" + color + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
||||||
|
padd + C.GRAY + "" + color + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
||||||
|
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
|
||||||
|
};
|
||||||
|
|
||||||
|
String[] info = new String[]{
|
||||||
|
"",
|
||||||
|
padd2 + color + " Iris, " + C.AQUA + "Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]",
|
||||||
|
padd2 + C.GRAY + " Version: " + color + version,
|
||||||
|
padd2 + C.GRAY + " By: " + color + "Volmit Software (Arcane Arts)",
|
||||||
|
padd2 + C.GRAY + " Server: " + color + serverVersion,
|
||||||
|
padd2 + C.GRAY + " Java: " + color + javaVersion + C.GRAY + " | Date: " + color + startupDate,
|
||||||
|
padd2 + C.GRAY + " Commit: " + color + BuildConstants.COMMIT + C.GRAY + "/" + color + BuildConstants.ENVIRONMENT,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder("\n\n");
|
||||||
|
for (int i = 0; i < splash.length; i++) {
|
||||||
|
builder.append(splash[i]);
|
||||||
|
if (i < info.length) {
|
||||||
|
builder.append(info[i]);
|
||||||
|
}
|
||||||
|
builder.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Iris.info(builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String wrap(String tag) {
|
||||||
|
return C.BOLD.toString() + C.DARK_GRAY + "[" + C.BOLD + color + tag + C.BOLD + C.DARK_GRAY + "]" + C.RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServerVersion() {
|
||||||
|
String version = Bukkit.getVersion();
|
||||||
|
int marker = version.indexOf(" (MC:");
|
||||||
|
if (marker != -1) {
|
||||||
|
return version.substring(0, marker);
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getJavaVersion() {
|
||||||
|
String version = System.getProperty("java.version");
|
||||||
|
if (version.startsWith("1.")) {
|
||||||
|
version = version.substring(2, 3);
|
||||||
|
} else {
|
||||||
|
int dot = version.indexOf('.');
|
||||||
|
if (dot != -1) {
|
||||||
|
version = version.substring(0, dot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.parseInt(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStartupDate() {
|
||||||
|
return LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getReleaseTrain(String version) {
|
||||||
|
String value = version;
|
||||||
|
int suffixIndex = value.indexOf('-');
|
||||||
|
if (suffixIndex >= 0) {
|
||||||
|
value = value.substring(0, suffixIndex);
|
||||||
|
}
|
||||||
|
String[] split = value.split("\\.");
|
||||||
|
if (split.length >= 2) {
|
||||||
|
return split[0] + "." + split[1];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package art.arcane.iris.core.safeguard.task;
|
||||||
|
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.iris.util.common.format.C;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Diagnostic {
|
||||||
|
private final Logger logger;
|
||||||
|
private final String message;
|
||||||
|
private final Throwable exception;
|
||||||
|
|
||||||
|
public Diagnostic(String message) {
|
||||||
|
this(Logger.ERROR, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic(Logger logger, String message) {
|
||||||
|
this(logger, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic(Logger logger, String message, Throwable exception) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.message = message;
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger getLogger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Throwable getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log() {
|
||||||
|
log(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(boolean withException) {
|
||||||
|
log(withException, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(boolean withException, boolean withStackTrace) {
|
||||||
|
logger.print(render(withException, withStackTrace));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render() {
|
||||||
|
return render(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render(boolean withException) {
|
||||||
|
return render(withException, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render(boolean withException, boolean withStackTrace) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(message);
|
||||||
|
if (withException && exception != null) {
|
||||||
|
builder.append(": ");
|
||||||
|
builder.append(exception);
|
||||||
|
if (withStackTrace) {
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
PrintStream ps = new PrintStream(os);
|
||||||
|
exception.printStackTrace(ps);
|
||||||
|
ps.flush();
|
||||||
|
builder.append("\n");
|
||||||
|
builder.append(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return C.strip(render());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Logger {
|
||||||
|
DEBUG(Iris::debug),
|
||||||
|
RAW(Iris::msg),
|
||||||
|
INFO(Iris::info),
|
||||||
|
WARN(Iris::warn),
|
||||||
|
ERROR(Iris::error);
|
||||||
|
|
||||||
|
private final Consumer<String> logger;
|
||||||
|
|
||||||
|
Logger(Consumer<String> logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(String message) {
|
||||||
|
String[] lines = message.split("\\n");
|
||||||
|
for (String line : lines) {
|
||||||
|
logger.accept(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic create(String message) {
|
||||||
|
return create(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic create(String message, Throwable exception) {
|
||||||
|
return new Diagnostic(this, message, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package art.arcane.iris.core.safeguard.task;
|
||||||
|
|
||||||
|
import art.arcane.iris.core.safeguard.Mode;
|
||||||
|
import art.arcane.volmlib.util.format.Form;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class Task {
|
||||||
|
private final String id;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public Task(String id) {
|
||||||
|
this(id, Form.capitalizeWords(id.replace(" ", "_").toLowerCase(Locale.ROOT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task(String id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ValueWithDiagnostics<Mode> run();
|
||||||
|
|
||||||
|
public static Task of(String id, String name, Supplier<ValueWithDiagnostics<Mode>> action) {
|
||||||
|
return new Task(id, name) {
|
||||||
|
@Override
|
||||||
|
public ValueWithDiagnostics<Mode> run() {
|
||||||
|
return action.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task of(String id, Supplier<ValueWithDiagnostics<Mode>> action) {
|
||||||
|
return new Task(id) {
|
||||||
|
@Override
|
||||||
|
public ValueWithDiagnostics<Mode> run() {
|
||||||
|
return action.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package art.arcane.iris.core.safeguard.task;
|
||||||
|
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.iris.core.IrisWorlds;
|
||||||
|
import art.arcane.iris.core.nms.INMS;
|
||||||
|
import art.arcane.iris.core.nms.v1X.NMSBinding1X;
|
||||||
|
import art.arcane.iris.core.safeguard.Mode;
|
||||||
|
import art.arcane.iris.engine.object.IrisDimension;
|
||||||
|
import art.arcane.iris.util.common.misc.getHardware;
|
||||||
|
import art.arcane.iris.util.project.agent.Agent;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public final class Tasks {
|
||||||
|
private static final Task MEMORY = Task.of("memory", () -> {
|
||||||
|
long mem = getHardware.getProcessMemory();
|
||||||
|
if (mem >= 3072L) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mem > 2048L) {
|
||||||
|
return withDiagnostics(Mode.STABLE,
|
||||||
|
Diagnostic.Logger.INFO.create("Memory Recommendation"),
|
||||||
|
Diagnostic.Logger.INFO.create("- 3GB+ process memory is recommended for Iris."),
|
||||||
|
Diagnostic.Logger.INFO.create("- Process Memory: " + mem + " MB"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.WARNING,
|
||||||
|
Diagnostic.Logger.WARN.create("Low Memory"),
|
||||||
|
Diagnostic.Logger.WARN.create("- Iris is running with 2GB or less process memory."),
|
||||||
|
Diagnostic.Logger.WARN.create("- 3GB+ process memory is recommended for Iris."),
|
||||||
|
Diagnostic.Logger.WARN.create("- Process Memory: " + mem + " MB"));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task INCOMPATIBILITIES = Task.of("incompatibilities", () -> {
|
||||||
|
Set<String> plugins = new HashSet<>(Set.of("dynmap", "Stratos"));
|
||||||
|
plugins.removeIf(name -> server().getPluginManager().getPlugin(name) == null);
|
||||||
|
|
||||||
|
if (plugins.isEmpty()) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Diagnostic> diagnostics = new ArrayList<>();
|
||||||
|
if (plugins.contains("dynmap")) {
|
||||||
|
addAllDiagnostics(diagnostics,
|
||||||
|
Diagnostic.Logger.ERROR.create("Dynmap"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- The plugin Dynmap is not compatible with the server."),
|
||||||
|
Diagnostic.Logger.ERROR.create("- If you want to have a map plugin like Dynmap, consider Bluemap."));
|
||||||
|
}
|
||||||
|
if (plugins.contains("Stratos")) {
|
||||||
|
addAllDiagnostics(diagnostics,
|
||||||
|
Diagnostic.Logger.ERROR.create("Stratos"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- Iris is not compatible with other worldgen plugins."));
|
||||||
|
}
|
||||||
|
return withDiagnostics(Mode.WARNING, diagnostics);
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task SOFTWARE = Task.of("software", () -> {
|
||||||
|
Set<String> supported = Set.of("canvas", "folia", "purpur", "pufferfish", "paper", "spigot", "bukkit");
|
||||||
|
String serverName = server().getName().toLowerCase(Locale.ROOT);
|
||||||
|
boolean supportedServer = isCanvasServer();
|
||||||
|
if (!supportedServer) {
|
||||||
|
for (String candidate : supported) {
|
||||||
|
if (serverName.contains(candidate)) {
|
||||||
|
supportedServer = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportedServer) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.WARNING,
|
||||||
|
Diagnostic.Logger.WARN.create("Unsupported Server Software"),
|
||||||
|
Diagnostic.Logger.WARN.create("- Please consider using Canvas, Folia, Paper, or Purpur instead."));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task VERSION = Task.of("version", () -> {
|
||||||
|
String[] parts = Iris.instance.getDescription().getVersion().split("-");
|
||||||
|
String supportedVersions;
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
String minVersion = parts[1];
|
||||||
|
String maxVersion = parts[2];
|
||||||
|
supportedVersions = minVersion.equals(maxVersion) ? minVersion : minVersion + " - " + maxVersion;
|
||||||
|
} else if (parts.length >= 2) {
|
||||||
|
supportedVersions = parts[1];
|
||||||
|
} else {
|
||||||
|
supportedVersions = "1.21.11";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(INMS.get() instanceof NMSBinding1X)) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.UNSTABLE,
|
||||||
|
Diagnostic.Logger.ERROR.create("Server Version"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- Iris only supports " + supportedVersions));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task INJECTION = Task.of("injection", () -> {
|
||||||
|
if (!isPaperPreferredServer() && !Agent.isInstalled()) {
|
||||||
|
return withDiagnostics(Mode.WARNING,
|
||||||
|
Diagnostic.Logger.WARN.create("Java Agent"),
|
||||||
|
Diagnostic.Logger.WARN.create("- Skipping dynamic Java agent attach on Spigot/Bukkit to avoid runtime agent warnings."),
|
||||||
|
Diagnostic.Logger.WARN.create("- For full runtime injection support, run with -javaagent:"
|
||||||
|
+ Agent.AGENT_JAR.getPath() + " or use Canvas/Folia/Paper/Purpur."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Agent.install()) {
|
||||||
|
return withDiagnostics(Mode.UNSTABLE,
|
||||||
|
Diagnostic.Logger.ERROR.create("Java Agent"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments."),
|
||||||
|
Diagnostic.Logger.ERROR.create("- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!INMS.get().injectBukkit()) {
|
||||||
|
return withDiagnostics(Mode.UNSTABLE,
|
||||||
|
Diagnostic.Logger.ERROR.create("Code Injection"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- Failed to inject code. Please contact support"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task DIMENSION_TYPES = Task.of("dimensionTypes", () -> {
|
||||||
|
Set<String> keys = IrisWorlds.get().getDimensions().map(IrisDimension::getDimensionTypeKey).collect(Collectors.toSet());
|
||||||
|
if (!INMS.get().missingDimensionTypes(keys.toArray(String[]::new))) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.UNSTABLE,
|
||||||
|
Diagnostic.Logger.ERROR.create("Dimension Types"),
|
||||||
|
Diagnostic.Logger.ERROR.create("- Required Iris dimension types were not loaded."),
|
||||||
|
Diagnostic.Logger.ERROR.create("- If this still happens after a restart please contact support."));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task DISK_SPACE = Task.of("diskSpace", () -> {
|
||||||
|
double freeGiB = server().getWorldContainer().getFreeSpace() / (double) 0x4000_0000;
|
||||||
|
if (freeGiB > 3.0) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.WARNING,
|
||||||
|
Diagnostic.Logger.WARN.create("Insufficient Disk Space"),
|
||||||
|
Diagnostic.Logger.WARN.create("- 3GB of free space is required for Iris to function."));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final Task JAVA = Task.of("java", () -> {
|
||||||
|
int version = Iris.getJavaVersion();
|
||||||
|
if (version == 21) {
|
||||||
|
return withDiagnostics(Mode.STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version > 21) {
|
||||||
|
return withDiagnostics(Mode.STABLE,
|
||||||
|
Diagnostic.Logger.INFO.create("Java Runtime"),
|
||||||
|
Diagnostic.Logger.INFO.create("- Running Java " + version + ". Iris is tested primarily on Java 21."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return withDiagnostics(Mode.WARNING,
|
||||||
|
Diagnostic.Logger.WARN.create("Unsupported Java version"),
|
||||||
|
Diagnostic.Logger.WARN.create("- Java 21+ is recommended. Current runtime: Java " + version));
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final List<Task> TASKS = List.of(
|
||||||
|
MEMORY,
|
||||||
|
INCOMPATIBILITIES,
|
||||||
|
SOFTWARE,
|
||||||
|
VERSION,
|
||||||
|
INJECTION,
|
||||||
|
DIMENSION_TYPES,
|
||||||
|
DISK_SPACE,
|
||||||
|
JAVA
|
||||||
|
);
|
||||||
|
|
||||||
|
private Tasks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Task> getTasks() {
|
||||||
|
return TASKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Server server() {
|
||||||
|
return Bukkit.getServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPaperPreferredServer() {
|
||||||
|
String name = server().getName().toLowerCase(Locale.ROOT);
|
||||||
|
return isCanvasServer()
|
||||||
|
|| name.contains("folia")
|
||||||
|
|| name.contains("paper")
|
||||||
|
|| name.contains("purpur")
|
||||||
|
|| name.contains("pufferfish");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCanvasServer() {
|
||||||
|
ClassLoader loader = server().getClass().getClassLoader();
|
||||||
|
try {
|
||||||
|
Class.forName("io.canvasmc.canvas.region.WorldRegionizer", false, loader);
|
||||||
|
return true;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return server().getName().toLowerCase(Locale.ROOT).contains("canvas");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addAllDiagnostics(List<Diagnostic> diagnostics, Diagnostic... values) {
|
||||||
|
for (Diagnostic value : values) {
|
||||||
|
diagnostics.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueWithDiagnostics<Mode> withDiagnostics(Mode mode, Diagnostic... diagnostics) {
|
||||||
|
return new ValueWithDiagnostics<>(mode, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueWithDiagnostics<Mode> withDiagnostics(Mode mode, List<Diagnostic> diagnostics) {
|
||||||
|
return new ValueWithDiagnostics<>(mode, diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package art.arcane.iris.core.safeguard.task;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ValueWithDiagnostics<T> {
|
||||||
|
private final T value;
|
||||||
|
private final List<Diagnostic> diagnostics;
|
||||||
|
|
||||||
|
public ValueWithDiagnostics(T value, List<Diagnostic> diagnostics) {
|
||||||
|
this.value = value;
|
||||||
|
this.diagnostics = List.copyOf(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueWithDiagnostics(T value, Diagnostic... diagnostics) {
|
||||||
|
this.value = value;
|
||||||
|
this.diagnostics = List.of(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Diagnostic> getDiagnostics() {
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log() {
|
||||||
|
log(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(boolean withException) {
|
||||||
|
log(withException, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(boolean withException, boolean withStackTrace) {
|
||||||
|
for (Diagnostic diagnostic : diagnostics) {
|
||||||
|
diagnostic.log(withException, withStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.environment;
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
|
||||||
import art.arcane.iris.core.scripting.func.UpdateExecutor;
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment;
|
|
||||||
import art.arcane.iris.engine.framework.Engine;
|
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public interface EngineEnvironment extends PackEnvironment {
|
|
||||||
static EngineEnvironment create(@NonNull Engine engine) {
|
|
||||||
return new IrisExecutionEnvironment(engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Engine getEngine();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Object spawnMob(@NonNull String script, @NonNull Location location);
|
|
||||||
|
|
||||||
void postSpawnMob(@NonNull String script, @NonNull Location location, @NonNull Entity mob);
|
|
||||||
|
|
||||||
void preprocessObject(@NonNull String script, @NonNull IrisRegistrant object);
|
|
||||||
|
|
||||||
void updateChunk(@NonNull String script, @NonNull MantleChunk<?> mantleChunk, @NonNull Chunk chunk, @NonNull UpdateExecutor executor);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.environment;
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisData;
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
|
|
||||||
import art.arcane.iris.engine.framework.Engine;
|
|
||||||
import art.arcane.volmlib.util.math.RNG;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public interface PackEnvironment extends SimpleEnvironment {
|
|
||||||
static PackEnvironment create(@NonNull IrisData data) {
|
|
||||||
return new IrisPackExecutionEnvironment(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
IrisData getData();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Object createNoise(@NonNull String script, @NonNull RNG rng);
|
|
||||||
|
|
||||||
EngineEnvironment with(@NonNull Engine engine);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.environment;
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface SimpleEnvironment {
|
|
||||||
static SimpleEnvironment create() {
|
|
||||||
return new IrisSimpleExecutionEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SimpleEnvironment create(@NonNull File projectDir) {
|
|
||||||
return new IrisSimpleExecutionEnvironment(projectDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void configureProject();
|
|
||||||
|
|
||||||
void execute(@NonNull String script);
|
|
||||||
|
|
||||||
void execute(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Object evaluate(@NonNull String script);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.func;
|
|
||||||
|
|
||||||
import art.arcane.iris.engine.object.IrisBiome;
|
|
||||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface BiomeLookup {
|
|
||||||
@BlockCoordinates
|
|
||||||
IrisBiome at(int x, int z);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.func;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface UpdateExecutor {
|
|
||||||
|
|
||||||
@NotNull Runnable wrap(int delay, @NotNull Runnable runnable);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
default Runnable wrap(@NotNull Runnable runnable) {
|
|
||||||
return wrap(1, runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void execute(@NotNull Runnable runnable) {
|
|
||||||
execute(1, runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void execute(int delay, @NotNull Runnable runnable) {
|
|
||||||
wrap(delay, runnable).run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,6 @@ import art.arcane.iris.core.loader.ResourceLoader;
|
|||||||
import art.arcane.iris.core.nms.container.BlockPos;
|
import art.arcane.iris.core.nms.container.BlockPos;
|
||||||
import art.arcane.iris.core.nms.container.Pair;
|
import art.arcane.iris.core.nms.container.Pair;
|
||||||
import art.arcane.iris.core.project.IrisProject;
|
import art.arcane.iris.core.project.IrisProject;
|
||||||
import art.arcane.iris.core.scripting.environment.EngineEnvironment;
|
|
||||||
import art.arcane.iris.core.service.PreservationSVC;
|
import art.arcane.iris.core.service.PreservationSVC;
|
||||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||||
@@ -97,7 +96,6 @@ public class IrisEngine implements Engine {
|
|||||||
private CompletableFuture<Long> hash32;
|
private CompletableFuture<Long> hash32;
|
||||||
private EngineMode mode;
|
private EngineMode mode;
|
||||||
private EngineEffects effects;
|
private EngineEffects effects;
|
||||||
private EngineEnvironment execution;
|
|
||||||
private EngineWorldManager worldManager;
|
private EngineWorldManager worldManager;
|
||||||
private volatile int parallelism;
|
private volatile int parallelism;
|
||||||
private boolean failing;
|
private boolean failing;
|
||||||
@@ -131,7 +129,6 @@ public class IrisEngine implements Engine {
|
|||||||
cleaning = new AtomicBoolean(false);
|
cleaning = new AtomicBoolean(false);
|
||||||
noisemapPrebakeRunning = new AtomicBoolean(false);
|
noisemapPrebakeRunning = new AtomicBoolean(false);
|
||||||
modeFallbackLogged = new AtomicBoolean(false);
|
modeFallbackLogged = new AtomicBoolean(false);
|
||||||
execution = getData().getEnvironment().with(this);
|
|
||||||
if (studio) {
|
if (studio) {
|
||||||
getData().dump();
|
getData().dump();
|
||||||
getData().clearLists();
|
getData().clearLists();
|
||||||
@@ -190,7 +187,6 @@ public class IrisEngine implements Engine {
|
|||||||
if (currentMode != null) {
|
if (currentMode != null) {
|
||||||
currentMode.close();
|
currentMode.close();
|
||||||
}
|
}
|
||||||
execution = getData().getEnvironment().with(this);
|
|
||||||
|
|
||||||
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
||||||
}
|
}
|
||||||
@@ -207,7 +203,6 @@ public class IrisEngine implements Engine {
|
|||||||
IrisWorldManager manager = new IrisWorldManager(this);
|
IrisWorldManager manager = new IrisWorldManager(this);
|
||||||
worldManager = manager;
|
worldManager = manager;
|
||||||
manager.startManager();
|
manager.startManager();
|
||||||
getDimension().getEngineScripts().forEach(execution::execute);
|
|
||||||
J.a(this::computeBiomeMaxes);
|
J.a(this::computeBiomeMaxes);
|
||||||
J.a(() -> {
|
J.a(() -> {
|
||||||
File[] roots = getData().getLoaders()
|
File[] roots = getData().getLoaders()
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ public final class IrisNoisemapPrebakePipeline {
|
|||||||
"ravines",
|
"ravines",
|
||||||
"mods",
|
"mods",
|
||||||
"expressions",
|
"expressions",
|
||||||
"scripts",
|
|
||||||
"images",
|
"images",
|
||||||
"snippet"
|
"snippet"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import art.arcane.iris.core.loader.IrisRegistrant;
|
|||||||
import art.arcane.iris.core.nms.container.BlockPos;
|
import art.arcane.iris.core.nms.container.BlockPos;
|
||||||
import art.arcane.iris.core.nms.container.Pair;
|
import art.arcane.iris.core.nms.container.Pair;
|
||||||
import art.arcane.iris.core.pregenerator.ChunkUpdater;
|
import art.arcane.iris.core.pregenerator.ChunkUpdater;
|
||||||
import art.arcane.iris.core.scripting.environment.EngineEnvironment;
|
|
||||||
import art.arcane.iris.core.service.ExternalDataSVC;
|
import art.arcane.iris.core.service.ExternalDataSVC;
|
||||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||||
import art.arcane.iris.engine.IrisComplex;
|
import art.arcane.iris.engine.IrisComplex;
|
||||||
@@ -114,8 +113,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
|
|
||||||
IrisContext getContext();
|
IrisContext getContext();
|
||||||
|
|
||||||
EngineEnvironment getExecution();
|
|
||||||
|
|
||||||
double getMaxBiomeObjectDensity();
|
double getMaxBiomeObjectDensity();
|
||||||
|
|
||||||
double getMaxBiomeDecoratorDensity();
|
double getMaxBiomeDecoratorDensity();
|
||||||
@@ -390,16 +387,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
}, RNG.r.i(1, 20))); //Why is there a random delay here?
|
}, RNG.r.i(1, 20))); //Why is there a random delay here?
|
||||||
});
|
});
|
||||||
|
|
||||||
chunk.raiseFlagUnchecked(MantleFlag.SCRIPT, () -> {
|
|
||||||
var scripts = getDimension().getChunkUpdateScripts();
|
|
||||||
if (scripts == null || scripts.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (var script : scripts) {
|
|
||||||
getExecution().updateChunk(script, chunk, c, (delay, task) -> run(semaphore, c, task, delay));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
semaphore.acquire(1024);
|
semaphore.acquire(1024);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
package art.arcane.iris.engine.mantle;
|
||||||
|
|
||||||
|
import art.arcane.iris.Iris;
|
||||||
|
import art.arcane.iris.core.IrisSettings;
|
||||||
|
import art.arcane.iris.core.nms.container.Pair;
|
||||||
|
import art.arcane.iris.engine.framework.Engine;
|
||||||
|
import art.arcane.iris.util.common.misc.RegenRuntime;
|
||||||
|
import art.arcane.iris.util.common.parallel.MultiBurst;
|
||||||
|
import art.arcane.iris.util.project.context.ChunkContext;
|
||||||
|
import art.arcane.iris.util.project.matter.TileWrapper;
|
||||||
|
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||||
|
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
||||||
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
|
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||||
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public interface MatterGenerator {
|
||||||
|
long REGEN_PASS_CACHE_TTL_MS = 600000L;
|
||||||
|
Executor DISPATCHER = MultiBurst.burst;
|
||||||
|
ConcurrentHashMap<String, Set<Long>> REGEN_GENERATED_CHUNKS_BY_PASS = new ConcurrentHashMap<>();
|
||||||
|
ConcurrentHashMap<String, Set<Long>> REGEN_CLEARED_CHUNKS_BY_PASS = new ConcurrentHashMap<>();
|
||||||
|
ConcurrentHashMap<String, Set<Long>> REGEN_PLANNED_CHUNKS_BY_PASS = new ConcurrentHashMap<>();
|
||||||
|
ConcurrentHashMap<String, Long> REGEN_PASS_TOUCHED_MS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
Engine getEngine();
|
||||||
|
|
||||||
|
Mantle<Matter> getMantle();
|
||||||
|
|
||||||
|
int getRadius();
|
||||||
|
|
||||||
|
int getRealRadius();
|
||||||
|
|
||||||
|
List<Pair<List<MantleComponent>, Integer>> getComponents();
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
|
||||||
|
if (!getEngine().getDimension().isUseMantle()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useMulticore = multicore || IrisSettings.get().getGenerator().isUseMulticoreMantle();
|
||||||
|
String threadName = Thread.currentThread().getName();
|
||||||
|
boolean regenThread = threadName.startsWith("Iris-Regen-");
|
||||||
|
boolean traceRegen = regenThread && IrisSettings.get().getGeneral().isDebug();
|
||||||
|
boolean forceRegen = regenThread;
|
||||||
|
String regenPassKey = forceRegen ? resolveRegenPassKey(threadName) : null;
|
||||||
|
boolean optimizedRegen = forceRegen && !IrisSettings.get().getGeneral().isDebug() && regenPassKey != null;
|
||||||
|
int writeRadius = optimizedRegen ? Math.min(getRadius(), getRealRadius()) : getRadius();
|
||||||
|
Set<Long> clearedChunks = optimizedRegen ? getRegenPassSet(REGEN_CLEARED_CHUNKS_BY_PASS, regenPassKey) : new HashSet<>();
|
||||||
|
Set<Long> plannedChunks = optimizedRegen ? getRegenPassSet(REGEN_PLANNED_CHUNKS_BY_PASS, regenPassKey) : null;
|
||||||
|
|
||||||
|
if (optimizedRegen) {
|
||||||
|
touchRegenPass(regenPassKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen matter start: center=" + x + "," + z
|
||||||
|
+ " radius=" + getRadius()
|
||||||
|
+ " realRadius=" + getRealRadius()
|
||||||
|
+ " writeRadius=" + writeRadius
|
||||||
|
+ " multicore=" + useMulticore
|
||||||
|
+ " components=" + getComponents().size()
|
||||||
|
+ " optimized=" + optimizedRegen
|
||||||
|
+ " passKey=" + (regenPassKey == null ? "none" : regenPassKey)
|
||||||
|
+ " thread=" + threadName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (MantleWriter writer = new MantleWriter(getEngine().getMantle(), getMantle(), x, z, writeRadius, useMulticore)) {
|
||||||
|
for (Pair<List<MantleComponent>, Integer> pair : getComponents()) {
|
||||||
|
int rawPassRadius = pair.getB();
|
||||||
|
int passRadius = optimizedRegen ? Math.min(rawPassRadius, writeRadius) : rawPassRadius;
|
||||||
|
String passFlags = pair.getA().stream().map(component -> component.getFlag().toString()).collect(Collectors.joining(","));
|
||||||
|
String passFlagKey = optimizedRegen ? regenPassKey + "|" + passFlags : null;
|
||||||
|
Set<Long> generatedChunks = passFlagKey == null ? null : getRegenPassSet(REGEN_GENERATED_CHUNKS_BY_PASS, passFlagKey);
|
||||||
|
int visitedChunks = 0;
|
||||||
|
int clearedCount = 0;
|
||||||
|
int plannedSkipped = 0;
|
||||||
|
int componentSkipped = 0;
|
||||||
|
int componentForcedReset = 0;
|
||||||
|
int launchedLayers = 0;
|
||||||
|
int dedupSkipped = 0;
|
||||||
|
List<CompletableFuture<Void>> launchedTasks = useMulticore ? new ArrayList<>() : null;
|
||||||
|
|
||||||
|
if (passFlagKey != null) {
|
||||||
|
touchRegenPass(passFlagKey);
|
||||||
|
}
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen matter pass start: center=" + x + "," + z
|
||||||
|
+ " passRadius=" + passRadius
|
||||||
|
+ " rawPassRadius=" + rawPassRadius
|
||||||
|
+ " flags=[" + passFlags + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = -passRadius; i <= passRadius; i++) {
|
||||||
|
for (int j = -passRadius; j <= passRadius; j++) {
|
||||||
|
int passX = x + i;
|
||||||
|
int passZ = z + j;
|
||||||
|
visitedChunks++;
|
||||||
|
long passKey = chunkKey(passX, passZ);
|
||||||
|
if (generatedChunks != null && !generatedChunks.add(passKey)) {
|
||||||
|
dedupSkipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MantleChunk<Matter> chunk = writer.acquireChunk(passX, passZ);
|
||||||
|
if (forceRegen) {
|
||||||
|
if (clearedChunks.add(passKey)) {
|
||||||
|
chunk.deleteSlices(BlockData.class);
|
||||||
|
chunk.deleteSlices(String.class);
|
||||||
|
chunk.deleteSlices(TileWrapper.class);
|
||||||
|
chunk.flag(MantleFlag.PLANNED, false);
|
||||||
|
clearedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forceRegen && chunk.isFlagged(MantleFlag.PLANNED)) {
|
||||||
|
plannedSkipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MantleComponent component : pair.getA()) {
|
||||||
|
if (!forceRegen && chunk.isFlagged(component.getFlag())) {
|
||||||
|
componentSkipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (forceRegen && chunk.isFlagged(component.getFlag())) {
|
||||||
|
chunk.flag(component.getFlag(), false);
|
||||||
|
componentForcedReset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
launchedLayers++;
|
||||||
|
int finalPassX = passX;
|
||||||
|
int finalPassZ = passZ;
|
||||||
|
MantleChunk<Matter> finalChunk = chunk;
|
||||||
|
MantleComponent finalComponent = component;
|
||||||
|
Runnable task = () -> finalChunk.raiseFlagUnchecked(finalComponent.getFlag(),
|
||||||
|
() -> finalComponent.generateLayer(writer, finalPassX, finalPassZ, context));
|
||||||
|
|
||||||
|
if (useMulticore) {
|
||||||
|
launchedTasks.add(CompletableFuture.runAsync(task, DISPATCHER));
|
||||||
|
} else {
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useMulticore) {
|
||||||
|
for (CompletableFuture<Void> launchedTask : launchedTasks) {
|
||||||
|
launchedTask.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen matter pass done: center=" + x + "," + z
|
||||||
|
+ " passRadius=" + passRadius
|
||||||
|
+ " rawPassRadius=" + rawPassRadius
|
||||||
|
+ " visited=" + visitedChunks
|
||||||
|
+ " cleared=" + clearedCount
|
||||||
|
+ " dedupSkipped=" + dedupSkipped
|
||||||
|
+ " plannedSkipped=" + plannedSkipped
|
||||||
|
+ " componentSkipped=" + componentSkipped
|
||||||
|
+ " componentForcedReset=" + componentForcedReset
|
||||||
|
+ " launchedLayers=" + launchedLayers
|
||||||
|
+ " flags=[" + passFlags + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = -getRealRadius(); i <= getRealRadius(); i++) {
|
||||||
|
for (int j = -getRealRadius(); j <= getRealRadius(); j++) {
|
||||||
|
int realX = x + i;
|
||||||
|
int realZ = z + j;
|
||||||
|
long realKey = chunkKey(realX, realZ);
|
||||||
|
if (plannedChunks != null && !plannedChunks.add(realKey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
writer.acquireChunk(realX, realZ).flag(MantleFlag.PLANNED, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen matter done: center=" + x + "," + z
|
||||||
|
+ " markedRealRadius=" + getRealRadius()
|
||||||
|
+ " forceRegen=" + forceRegen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long chunkKey(int x, int z) {
|
||||||
|
return (((long) x) << 32) ^ (z & 0xffffffffL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<Long> getRegenPassSet(ConcurrentHashMap<String, Set<Long>> store, String passKey) {
|
||||||
|
return store.computeIfAbsent(passKey, key -> ConcurrentHashMap.newKeySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveRegenPassKey(String threadName) {
|
||||||
|
String runtimeKey = RegenRuntime.getRunId();
|
||||||
|
if (runtimeKey != null && !runtimeKey.isBlank()) {
|
||||||
|
return runtimeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!threadName.startsWith("Iris-Regen-")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String suffix = threadName.substring("Iris-Regen-".length());
|
||||||
|
int lastDash = suffix.lastIndexOf('-');
|
||||||
|
if (lastDash <= 0) {
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
return suffix.substring(0, lastDash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void touchRegenPass(String passKey) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
REGEN_PASS_TOUCHED_MS.put(passKey, now);
|
||||||
|
if (REGEN_PASS_TOUCHED_MS.size() <= 64) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Map.Entry<String, Long>> iterator = REGEN_PASS_TOUCHED_MS.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, Long> entry = iterator.next();
|
||||||
|
if (now - entry.getValue() <= REGEN_PASS_CACHE_TTL_MS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = entry.getKey();
|
||||||
|
iterator.remove();
|
||||||
|
REGEN_GENERATED_CHUNKS_BY_PASS.remove(key);
|
||||||
|
REGEN_CLEARED_CHUNKS_BY_PASS.remove(key);
|
||||||
|
REGEN_PLANNED_CHUNKS_BY_PASS.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,7 +78,6 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
private final transient AtomicCache<Double> cosr = new AtomicCache<>();
|
private final transient AtomicCache<Double> cosr = new AtomicCache<>();
|
||||||
private final transient AtomicCache<Double> rad = new AtomicCache<>();
|
private final transient AtomicCache<Double> rad = new AtomicCache<>();
|
||||||
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
|
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KMap<String, KList<String>>> cachedPreProcessors = new AtomicCache<>();
|
|
||||||
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
|
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
|
||||||
@MinNumber(2)
|
@MinNumber(2)
|
||||||
@Required
|
@Required
|
||||||
@@ -248,25 +247,10 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
@MaxNumber(318)
|
@MaxNumber(318)
|
||||||
@Desc("The Subterrain Fluid Layer Height")
|
@Desc("The Subterrain Fluid Layer Height")
|
||||||
private int caveLavaHeight = 8;
|
private int caveLavaHeight = 8;
|
||||||
@RegistryListFunction(ComponentFlagFunction.class)
|
@RegistryListFunction(ComponentFlagFunction.class)
|
||||||
@ArrayType(type = String.class)
|
@ArrayType(type = String.class)
|
||||||
@Desc("Collection of disabled components")
|
@Desc("Collection of disabled components")
|
||||||
private KList<MantleFlag> disabledComponents = new KList<>();
|
private KList<MantleFlag> disabledComponents = new KList<>();
|
||||||
@Desc("A list of globally applied pre-processors")
|
|
||||||
@ArrayType(type = IrisPreProcessors.class)
|
|
||||||
private KList<IrisPreProcessors> globalPreProcessors = new KList<>();
|
|
||||||
@Desc("A list of scripts executed on engine setup\nFile extension: .engine.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@ArrayType(type = String.class, min = 1)
|
|
||||||
private KList<String> engineScripts = new KList<>();
|
|
||||||
@Desc("A list of scripts executed on data setup\nFile extension: .data.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@ArrayType(type = String.class, min = 1)
|
|
||||||
private KList<String> dataScripts = new KList<>();
|
|
||||||
@Desc("A list of scripts executed on chunk update\nFile extension: .update.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@ArrayType(type = String.class, min = 1)
|
|
||||||
private KList<String> chunkUpdateScripts = new KList<>();
|
|
||||||
|
|
||||||
public int getMaxHeight() {
|
public int getMaxHeight() {
|
||||||
return (int) getDimensionHeight().getMax();
|
return (int) getDimensionHeight().getMax();
|
||||||
@@ -398,17 +382,6 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<String> getPreProcessors(String type) {
|
|
||||||
return cachedPreProcessors.aquire(() -> {
|
|
||||||
KMap<String, KList<String>> preProcessors = new KMap<>();
|
|
||||||
for (var entry : globalPreProcessors) {
|
|
||||||
preProcessors.computeIfAbsent(entry.getType(), k -> new KList<>())
|
|
||||||
.addAll(entry.getScripts());
|
|
||||||
}
|
|
||||||
return preProcessors;
|
|
||||||
}).get(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IrisGeneratorStyle getBiomeStyle(InferredType type) {
|
public IrisGeneratorStyle getBiomeStyle(InferredType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CAVE:
|
case CAVE:
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package art.arcane.iris.engine.object;
|
|||||||
import art.arcane.iris.engine.framework.Engine;
|
import art.arcane.iris.engine.framework.Engine;
|
||||||
import art.arcane.iris.engine.framework.EngineMode;
|
import art.arcane.iris.engine.framework.EngineMode;
|
||||||
import art.arcane.iris.engine.object.annotations.Desc;
|
import art.arcane.iris.engine.object.annotations.Desc;
|
||||||
import art.arcane.iris.engine.object.annotations.RegistryListResource;
|
|
||||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -38,19 +37,7 @@ public class IrisDimensionMode {
|
|||||||
@Desc("The dimension type")
|
@Desc("The dimension type")
|
||||||
private IrisDimensionModeType type = IrisDimensionModeType.OVERWORLD;
|
private IrisDimensionModeType type = IrisDimensionModeType.OVERWORLD;
|
||||||
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@Desc("The script to create the dimension mode instead of using provided types\nFile extension: .engine.kts")
|
|
||||||
private String script;
|
|
||||||
|
|
||||||
public EngineMode create(Engine engine) {
|
public EngineMode create(Engine engine) {
|
||||||
if (script == null) {
|
return type.create(engine);
|
||||||
return type.create(engine);
|
|
||||||
}
|
|
||||||
Object result = engine.getExecution().evaluate(script);
|
|
||||||
if (result instanceof EngineMode) {
|
|
||||||
return (EngineMode) result;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException("The script '" + script + "' did not return an engine mode!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,18 +163,9 @@ public class IrisEntity extends IrisRegistrant {
|
|||||||
@Desc("Create a mob from another plugin, such as Mythic Mobs. Should be in the format of a namespace of PluginName:MobName")
|
@Desc("Create a mob from another plugin, such as Mythic Mobs. Should be in the format of a namespace of PluginName:MobName")
|
||||||
private String specialType = "";
|
private String specialType = "";
|
||||||
|
|
||||||
@Desc("Set to true if you want to apply all of the settings here to the mob, even though an external plugin has already done so. Scripts are always applied.")
|
@Desc("Set to true if you want to apply all of the settings here to the mob, even though an external plugin has already done so.")
|
||||||
private boolean applySettingsToCustomMobAnyways = false;
|
private boolean applySettingsToCustomMobAnyways = false;
|
||||||
|
|
||||||
@Desc("Set the entity type to UNKNOWN, then define a script here which ends with the entity variable (the result). You can use location to find the target location. You can spawn any entity this way.\nFile extension: .spawn.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
private String spawnerScript = "";
|
|
||||||
|
|
||||||
@ArrayType(min = 1, type = String.class)
|
|
||||||
@Desc("Executed post spawn you can modify the entity however you want with it\nFile extension: .postspawn.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
private KList<String> postSpawnScripts = new KList<>();
|
|
||||||
|
|
||||||
@ArrayType(min = 1, type = IrisCommand.class)
|
@ArrayType(min = 1, type = IrisCommand.class)
|
||||||
@Desc("Run raw commands when this entity is spawned. Use {x}, {y}, and {z} for location. /summon pig {x} {y} {z}")
|
@Desc("Run raw commands when this entity is spawned. Use {x}, {y}, and {z} for location. /summon pig {x} {y} {z}")
|
||||||
private KList<IrisCommand> rawCommands = new KList<>();
|
private KList<IrisCommand> rawCommands = new KList<>();
|
||||||
@@ -211,17 +202,6 @@ public class IrisEntity extends IrisRegistrant {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spawnerScript.isEmpty() && ee == null) {
|
|
||||||
synchronized (this) {
|
|
||||||
try {
|
|
||||||
ee = (Entity) gen.getExecution().spawnMob(spawnerScript, at);
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
Iris.error("You must return an Entity in your scripts to use entity scripts!");
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSpecialType() && !applySettingsToCustomMobAnyways) {
|
if (isSpecialType() && !applySettingsToCustomMobAnyways) {
|
||||||
return ee;
|
return ee;
|
||||||
}
|
}
|
||||||
@@ -356,14 +336,6 @@ public class IrisEntity extends IrisRegistrant {
|
|||||||
spawnEffect.apply(e);
|
spawnEffect.apply(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postSpawnScripts.isNotEmpty()) {
|
|
||||||
synchronized (this) {
|
|
||||||
for (String i : postSpawnScripts) {
|
|
||||||
gen.getExecution().postSpawnMob(i, at, ee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawCommands.isNotEmpty()) {
|
if (rawCommands.isNotEmpty()) {
|
||||||
final Location fat = at;
|
final Location fat = at;
|
||||||
rawCommands.forEach(r -> r.run(fat));
|
rawCommands.forEach(r -> r.run(fat));
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ public class IrisExternalDatapack {
|
|||||||
@Desc("Optional structure-set alias mappings used to synthesize vanilla structure_set replacements from non-minecraft source keys")
|
@Desc("Optional structure-set alias mappings used to synthesize vanilla structure_set replacements from non-minecraft source keys")
|
||||||
private KList<IrisExternalDatapackStructureSetAlias> structureSetAliases = new KList<>();
|
private KList<IrisExternalDatapackStructureSetAlias> structureSetAliases = new KList<>();
|
||||||
|
|
||||||
|
@ArrayType(type = IrisExternalDatapackTemplateAlias.class, min = 1)
|
||||||
|
@Desc("Optional template location alias mappings applied while projecting template pools")
|
||||||
|
private KList<IrisExternalDatapackTemplateAlias> templateAliases = new KList<>();
|
||||||
|
|
||||||
@ArrayType(type = IrisExternalDatapackStructurePatch.class, min = 1)
|
@ArrayType(type = IrisExternalDatapackStructurePatch.class, min = 1)
|
||||||
@Desc("Structure placement patches applied when this external datapack is projected")
|
@Desc("Structure placement patches applied when this external datapack is projected")
|
||||||
private KList<IrisExternalDatapackStructurePatch> structurePatches = new KList<>();
|
private KList<IrisExternalDatapackStructurePatch> structurePatches = new KList<>();
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package art.arcane.iris.engine.object;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.annotations.Desc;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Desc("Maps missing template-pool element locations from an external datapack to replacement template locations")
|
||||||
|
public class IrisExternalDatapackTemplateAlias {
|
||||||
|
@Desc("Source template location to rewrite")
|
||||||
|
private String from = "";
|
||||||
|
|
||||||
|
@Desc("Target template location. Use minecraft:empty to convert the element to an empty pool element")
|
||||||
|
private String to = "";
|
||||||
|
|
||||||
|
@Desc("Enable or disable this alias entry")
|
||||||
|
private boolean enabled = true;
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import art.arcane.volmlib.util.math.RNG;
|
|||||||
import art.arcane.iris.util.project.noise.CNG;
|
import art.arcane.iris.util.project.noise.CNG;
|
||||||
import art.arcane.iris.util.project.noise.ExpressionNoise;
|
import art.arcane.iris.util.project.noise.ExpressionNoise;
|
||||||
import art.arcane.iris.util.project.noise.ImageNoise;
|
import art.arcane.iris.util.project.noise.ImageNoise;
|
||||||
import art.arcane.iris.util.project.noise.NoiseGenerator;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -59,9 +58,6 @@ public class IrisGeneratorStyle {
|
|||||||
private String expression = null;
|
private String expression = null;
|
||||||
@Desc("Use an Image map instead of a generated value")
|
@Desc("Use an Image map instead of a generated value")
|
||||||
private IrisImageMap imageMap = null;
|
private IrisImageMap imageMap = null;
|
||||||
@Desc("Instead of using the style property, use a custom noise generator to represent this style.\nFile extension: .noise.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
private String script = null;
|
|
||||||
@MinNumber(0.00001)
|
@MinNumber(0.00001)
|
||||||
@Desc("The Output multiplier. Only used if parent is fracture.")
|
@Desc("The Output multiplier. Only used if parent is fracture.")
|
||||||
private double multiplier = 1;
|
private double multiplier = 1;
|
||||||
@@ -111,7 +107,7 @@ public class IrisGeneratorStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int hash() {
|
private int hash() {
|
||||||
return Objects.hash(expression, imageMapHash(), script, multiplier, axialFracturing, fracture != null ? fracture.hash() : 0, exponent, cacheSize, zoom, cellularZoom, cellularFrequency, style);
|
return Objects.hash(expression, imageMapHash(), multiplier, axialFracturing, fracture != null ? fracture.hash() : 0, exponent, cacheSize, zoom, cellularZoom, cellularFrequency, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int prebakeSignature() {
|
public int prebakeSignature() {
|
||||||
@@ -129,19 +125,6 @@ public class IrisGeneratorStyle {
|
|||||||
return cachePrefix(rng, effectiveCacheSize) + Long.toUnsignedString(sourceStamp);
|
return cachePrefix(rng, effectiveCacheSize) + Long.toUnsignedString(sourceStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long scriptStamp(IrisData data) {
|
|
||||||
if (getScript() == null) {
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
File scriptFile = data.getScriptLoader().findFile(getScript());
|
|
||||||
if (scriptFile == null) {
|
|
||||||
return Integer.toUnsignedLong(getScript().hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.toUnsignedLong(Objects.hash(getScript(), scriptFile.lastModified(), scriptFile.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearStaleCacheEntries(IrisData data, String prefix, String key) {
|
private void clearStaleCacheEntries(IrisData data, String prefix, String key) {
|
||||||
File cacheFolder = new File(data.getDataFolder(), ".cache");
|
File cacheFolder = new File(data.getDataFolder(), ".cache");
|
||||||
File[] files = cacheFolder.listFiles((dir, name) -> name.endsWith(".cnm") && name.startsWith(prefix));
|
File[] files = cacheFolder.listFiles((dir, name) -> name.endsWith(".cnm") && name.startsWith(prefix));
|
||||||
@@ -178,13 +161,6 @@ public class IrisGeneratorStyle {
|
|||||||
} else if (getImageMap() != null) {
|
} else if (getImageMap() != null) {
|
||||||
cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake();
|
cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake();
|
||||||
sourceStamp = Integer.toUnsignedLong(imageMapHash());
|
sourceStamp = Integer.toUnsignedLong(imageMapHash());
|
||||||
} else if (getScript() != null) {
|
|
||||||
Object result = data.getEnvironment().createNoise(getScript(), rng);
|
|
||||||
if (result == null) Iris.warn("Failed to create noise from script: " + getScript());
|
|
||||||
if (result instanceof NoiseGenerator generator) {
|
|
||||||
cng = new CNG(rng, generator, 1D, 1).bake();
|
|
||||||
sourceStamp = scriptStamp(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cng == null) {
|
if (cng == null) {
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package art.arcane.iris.engine.object;
|
|
||||||
|
|
||||||
import art.arcane.iris.engine.object.annotations.*;
|
|
||||||
import art.arcane.iris.engine.object.annotations.functions.ResourceLoadersFunction;
|
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@Desc("Represents global preprocessors")
|
|
||||||
public class IrisPreProcessors {
|
|
||||||
@Required
|
|
||||||
@Desc("The preprocessor type")
|
|
||||||
@RegistryListFunction(ResourceLoadersFunction.class)
|
|
||||||
private String type = "dimension";
|
|
||||||
|
|
||||||
@Required
|
|
||||||
@Desc("The preprocessor scripts\nFile extension: .proc.kts")
|
|
||||||
@RegistryListResource(IrisScript.class)
|
|
||||||
@ArrayType(type = String.class, min = 1)
|
|
||||||
private KList<String> scripts = new KList<>();
|
|
||||||
}
|
|
||||||
@@ -1,58 +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 art.arcane.iris.engine.object;
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
|
||||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public class IrisScript extends IrisRegistrant {
|
|
||||||
private final String source;
|
|
||||||
|
|
||||||
public IrisScript() {
|
|
||||||
this("");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IrisScript(String source) {
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFolderName() {
|
|
||||||
return "scripts";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTypeName() {
|
|
||||||
return "Script";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void scanForErrors(JSONObject p, VolmitSender sender) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -72,9 +72,4 @@ public class IrisVanillaLootTable extends IrisLootTable {
|
|||||||
public IrisData getLoader() {
|
public IrisData getLoader() {
|
||||||
throw new UnsupportedOperationException("VanillaLootTables do not have a loader");
|
throw new UnsupportedOperationException("VanillaLootTables do not have a loader");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<String> getPreprocessors() {
|
|
||||||
return new KList<>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,9 +114,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
this.folder = new ReactiveFolder(
|
this.folder = new ReactiveFolder(
|
||||||
dataLocation,
|
dataLocation,
|
||||||
(_a, _b, _c) -> hotload(),
|
(_a, _b, _c) -> hotload(),
|
||||||
new KList<>(".iob", ".json", ".kts"),
|
new KList<>(".iob", ".json"),
|
||||||
new KList<>(".iris"),
|
new KList<>(".iris"),
|
||||||
new KList<>(".gradle.kts")
|
new KList<>()
|
||||||
);
|
);
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
|
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +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 art.arcane.iris.util.common.director.handlers;
|
|
||||||
|
|
||||||
import art.arcane.iris.engine.object.IrisScript;
|
|
||||||
import art.arcane.iris.util.common.director.specialhandlers.RegistrantHandler;
|
|
||||||
|
|
||||||
public class ScriptHandler extends RegistrantHandler<IrisScript> {
|
|
||||||
public ScriptHandler() {
|
|
||||||
super(IrisScript.class, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRandomDefault() {
|
|
||||||
return "script";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package art.arcane.iris.util.project.context;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.IrisComplex;
|
||||||
|
import art.arcane.iris.engine.object.IrisBiome;
|
||||||
|
import art.arcane.iris.engine.object.IrisRegion;
|
||||||
|
import art.arcane.iris.util.common.parallel.MultiBurst;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
public class ChunkContext {
|
||||||
|
private final int x;
|
||||||
|
private final int z;
|
||||||
|
private final ChunkedDataCache<Double> height;
|
||||||
|
private final ChunkedDataCache<IrisBiome> biome;
|
||||||
|
private final ChunkedDataCache<IrisBiome> cave;
|
||||||
|
private final ChunkedDataCache<BlockData> rock;
|
||||||
|
private final ChunkedDataCache<BlockData> fluid;
|
||||||
|
private final ChunkedDataCache<IrisRegion> region;
|
||||||
|
|
||||||
|
public ChunkContext(int x, int z, IrisComplex complex) {
|
||||||
|
this(x, z, complex, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkContext(int x, int z, IrisComplex complex, boolean cache) {
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
this.height = new ChunkedDataCache<>(complex.getHeightStream(), x, z, cache);
|
||||||
|
this.biome = new ChunkedDataCache<>(complex.getTrueBiomeStream(), x, z, cache);
|
||||||
|
this.cave = new ChunkedDataCache<>(complex.getCaveBiomeStream(), x, z, cache);
|
||||||
|
this.rock = new ChunkedDataCache<>(complex.getRockStream(), x, z, cache);
|
||||||
|
this.fluid = new ChunkedDataCache<>(complex.getFluidStream(), x, z, cache);
|
||||||
|
this.region = new ChunkedDataCache<>(complex.getRegionStream(), x, z, cache);
|
||||||
|
|
||||||
|
if (cache) {
|
||||||
|
Executor executor = MultiBurst.burst;
|
||||||
|
List<CompletableFuture<Void>> tasks = new ArrayList<>(6);
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> height.fill(executor), executor));
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> biome.fill(executor), executor));
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> cave.fill(executor), executor));
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> rock.fill(executor), executor));
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> fluid.fill(executor), executor));
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> region.fill(executor), executor));
|
||||||
|
for (CompletableFuture<Void> task : tasks) {
|
||||||
|
task.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getZ() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<Double> getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<IrisBiome> getBiome() {
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<IrisBiome> getCave() {
|
||||||
|
return cave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<BlockData> getRock() {
|
||||||
|
return rock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<BlockData> getFluid() {
|
||||||
|
return fluid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkedDataCache<IrisRegion> getRegion() {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package art.arcane.iris.util.project.context;
|
||||||
|
|
||||||
|
import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||||
|
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
|
||||||
|
public class ChunkedDataCache<T> {
|
||||||
|
private final int x;
|
||||||
|
private final int z;
|
||||||
|
private final ProceduralStream<T> stream;
|
||||||
|
private final boolean cache;
|
||||||
|
private final Object[] data;
|
||||||
|
|
||||||
|
@BlockCoordinates
|
||||||
|
public ChunkedDataCache(ProceduralStream<T> stream, int x, int z) {
|
||||||
|
this(stream, x, z, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BlockCoordinates
|
||||||
|
public ChunkedDataCache(ProceduralStream<T> stream, int x, int z, boolean cache) {
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
this.stream = stream;
|
||||||
|
this.cache = cache;
|
||||||
|
this.data = new Object[cache ? 256 : 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fill() {
|
||||||
|
fill(ForkJoinPool.commonPool());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fill(Executor executor) {
|
||||||
|
if (!cache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CompletableFuture<Void>> tasks = new ArrayList<>(16);
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
int row = j;
|
||||||
|
tasks.add(CompletableFuture.runAsync(() -> {
|
||||||
|
int rowOffset = row * 16;
|
||||||
|
double zz = (z + row);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
data[rowOffset + i] = stream.get(x + i, zz);
|
||||||
|
}
|
||||||
|
}, executor));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CompletableFuture<Void> task : tasks) {
|
||||||
|
task.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BlockCoordinates
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(int x, int z) {
|
||||||
|
if (!cache) {
|
||||||
|
return stream.get(this.x + x, this.z + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
T value = (T) data[(z * 16) + x];
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream.get(this.x + x, this.z + z);
|
||||||
|
}
|
||||||
|
}
|
||||||
-232
@@ -1,232 +0,0 @@
|
|||||||
package art.arcane.iris.core.pregenerator.cache
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.volmlib.util.data.Varint
|
|
||||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates
|
|
||||||
import art.arcane.volmlib.util.documentation.RegionCoordinates
|
|
||||||
import art.arcane.volmlib.util.io.IO
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream
|
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream
|
|
||||||
import java.io.*
|
|
||||||
|
|
||||||
class PregenCacheImpl(
|
|
||||||
private val directory: File,
|
|
||||||
private val maxSize: Int
|
|
||||||
) : PregenCache {
|
|
||||||
private val cache = Object2ObjectLinkedOpenHashMap<Pair<Int, Int>, Plate>()
|
|
||||||
|
|
||||||
@ChunkCoordinates
|
|
||||||
override fun isChunkCached(x: Int, z: Int): Boolean {
|
|
||||||
return this[x shr 10, z shr 10].isCached(
|
|
||||||
(x shr 5) and 31,
|
|
||||||
(z shr 5) and 31
|
|
||||||
) { isCached(x and 31, z and 31) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegionCoordinates
|
|
||||||
override fun isRegionCached(x: Int, z: Int): Boolean {
|
|
||||||
return this[x shr 5, z shr 5].isCached(
|
|
||||||
x and 31,
|
|
||||||
z and 31,
|
|
||||||
Region::isCached
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ChunkCoordinates
|
|
||||||
override fun cacheChunk(x: Int, z: Int) {
|
|
||||||
this[x shr 10, z shr 10].cache(
|
|
||||||
(x shr 5) and 31,
|
|
||||||
(z shr 5) and 31
|
|
||||||
) { cache(x and 31, z and 31) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegionCoordinates
|
|
||||||
override fun cacheRegion(x: Int, z: Int) {
|
|
||||||
this[x shr 5, z shr 5].cache(
|
|
||||||
x and 31,
|
|
||||||
z and 31,
|
|
||||||
Region::cache
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write() {
|
|
||||||
if (cache.isEmpty()) return
|
|
||||||
runBlocking {
|
|
||||||
for (plate in cache.values) {
|
|
||||||
if (!plate.dirty) continue
|
|
||||||
launch(dispatcher) {
|
|
||||||
writePlate(plate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun trim(unloadDuration: Long) {
|
|
||||||
if (cache.isEmpty()) return
|
|
||||||
val threshold = System.currentTimeMillis() - unloadDuration
|
|
||||||
runBlocking {
|
|
||||||
val it = cache.values.iterator()
|
|
||||||
while (it.hasNext()) {
|
|
||||||
val plate = it.next()
|
|
||||||
if (plate.lastAccess < threshold) it.remove()
|
|
||||||
launch(dispatcher) {
|
|
||||||
writePlate(plate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private operator fun get(x: Int, z: Int): Plate {
|
|
||||||
val key = x to z
|
|
||||||
val plate = cache.getAndMoveToFirst(key)
|
|
||||||
if (plate != null) return plate
|
|
||||||
return readPlate(x, z).also {
|
|
||||||
cache.putAndMoveToFirst(key, it)
|
|
||||||
runBlocking {
|
|
||||||
while (cache.size > maxSize) {
|
|
||||||
val plate = cache.removeLast()
|
|
||||||
launch(dispatcher) {
|
|
||||||
writePlate(plate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readPlate(x: Int, z: Int): Plate {
|
|
||||||
val file = fileForPlate(x, z)
|
|
||||||
if (!file.exists()) return Plate(x, z)
|
|
||||||
try {
|
|
||||||
DataInputStream(LZ4BlockInputStream(file.inputStream())).use {
|
|
||||||
return readPlate(x, z, it)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Iris.error("Failed to read pregen cache $file")
|
|
||||||
e.printStackTrace()
|
|
||||||
Iris.reportError(e)
|
|
||||||
}
|
|
||||||
return Plate(x, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writePlate(plate: Plate) {
|
|
||||||
if (!plate.dirty) return
|
|
||||||
val file = fileForPlate(plate.x, plate.z)
|
|
||||||
try {
|
|
||||||
IO.write(file, { DataOutputStream(LZ4BlockOutputStream(it)) }, plate::write)
|
|
||||||
plate.dirty = false
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Iris.error("Failed to write preen cache $file")
|
|
||||||
e.printStackTrace()
|
|
||||||
Iris.reportError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fileForPlate(x: Int, z: Int): File {
|
|
||||||
check(!(!directory.exists() && !directory.mkdirs())) { "Cannot create directory: " + directory.absolutePath }
|
|
||||||
return File(directory, "c.$x.$z.lz4b")
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Plate(
|
|
||||||
val x: Int,
|
|
||||||
val z: Int,
|
|
||||||
private var count: Short = 0,
|
|
||||||
private var regions: Array<Region?>? = arrayOfNulls(1024)
|
|
||||||
) {
|
|
||||||
var dirty: Boolean = false
|
|
||||||
var lastAccess: Long = System.currentTimeMillis()
|
|
||||||
|
|
||||||
fun cache(x: Int, z: Int, predicate: Region.() -> Boolean): Boolean {
|
|
||||||
lastAccess = System.currentTimeMillis()
|
|
||||||
if (count == SIZE) return false
|
|
||||||
val region = regions!!.run { this[x * 32 + z] ?: Region().also { this[x * 32 + z] = it } }
|
|
||||||
if (!region.predicate()) return false
|
|
||||||
if (++count == SIZE) regions = null
|
|
||||||
dirty = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isCached(x: Int, z: Int, predicate: Region.() -> Boolean): Boolean {
|
|
||||||
lastAccess = System.currentTimeMillis()
|
|
||||||
if (count == SIZE) return true
|
|
||||||
val region = regions!![x * 32 + z] ?: return false
|
|
||||||
return region.predicate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(dos: DataOutput) {
|
|
||||||
Varint.writeSignedVarInt(count.toInt(), dos)
|
|
||||||
regions?.forEach {
|
|
||||||
dos.writeBoolean(it == null)
|
|
||||||
it?.write(dos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Region(
|
|
||||||
private var count: Short = 0,
|
|
||||||
private var words: LongArray? = LongArray(64)
|
|
||||||
) {
|
|
||||||
fun cache(): Boolean {
|
|
||||||
if (count == SIZE) return false
|
|
||||||
count = SIZE
|
|
||||||
words = null
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cache(x: Int, z: Int): Boolean {
|
|
||||||
if (count == SIZE) return false
|
|
||||||
val words = words ?: return false
|
|
||||||
val i = x * 32 + z
|
|
||||||
val w = i shr 6
|
|
||||||
val b = 1L shl (i and 63)
|
|
||||||
|
|
||||||
val cur = (words[w] and b) != 0L
|
|
||||||
if (cur) return false
|
|
||||||
|
|
||||||
if (++count == SIZE) {
|
|
||||||
this.words = null
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
words[w] = words[w] or b
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isCached(): Boolean = count == SIZE
|
|
||||||
fun isCached(x: Int, z: Int): Boolean {
|
|
||||||
val i = x * 32 + z
|
|
||||||
return count == SIZE || (words!![i shr 6] and (1L shl (i and 63))) != 0L
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun write(dos: DataOutput) {
|
|
||||||
Varint.writeSignedVarInt(count.toInt(), dos)
|
|
||||||
words?.forEach { Varint.writeUnsignedVarLong(it, dos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val dispatcher = Dispatchers.IO.limitedParallelism(4)
|
|
||||||
private const val SIZE: Short = 1024
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun readPlate(x: Int, z: Int, din: DataInput): Plate {
|
|
||||||
val count = Varint.readSignedVarInt(din)
|
|
||||||
if (count == 1024) return Plate(x, z, SIZE, null)
|
|
||||||
return Plate(x, z, count.toShort(), Array(1024) {
|
|
||||||
if (din.readBoolean()) null
|
|
||||||
else readRegion(din)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun readRegion(din: DataInput): Region {
|
|
||||||
val count = Varint.readSignedVarInt(din)
|
|
||||||
return if (count == 1024) Region(SIZE, null)
|
|
||||||
else Region(count.toShort(), LongArray(64) { Varint.readUnsignedVarLong(din) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package art.arcane.iris.core.safeguard
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.core.safeguard.task.Diagnostic
|
|
||||||
import art.arcane.iris.core.safeguard.task.Task
|
|
||||||
import art.arcane.iris.core.safeguard.task.ValueWithDiagnostics
|
|
||||||
import art.arcane.iris.core.safeguard.task.tasks
|
|
||||||
import art.arcane.iris.util.common.format.C
|
|
||||||
import art.arcane.iris.util.common.scheduling.J
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object IrisSafeguard {
|
|
||||||
@Volatile
|
|
||||||
private var forceShutdown = false
|
|
||||||
private var results: Map<Task, ValueWithDiagnostics<Mode>> = emptyMap()
|
|
||||||
private var context: Map<String, String> = emptyMap()
|
|
||||||
private var attachment: Map<String, List<String>> = emptyMap()
|
|
||||||
private var mode = Mode.STABLE
|
|
||||||
private var count = 0
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun execute() {
|
|
||||||
val results = LinkedHashMap<Task, ValueWithDiagnostics<Mode>>(tasks.size)
|
|
||||||
val context = LinkedHashMap<String, String>(tasks.size)
|
|
||||||
val attachment = LinkedHashMap<String, List<String>>(tasks.size)
|
|
||||||
var mode = Mode.STABLE
|
|
||||||
var count = 0
|
|
||||||
for (task in tasks) {
|
|
||||||
var result: ValueWithDiagnostics<Mode>
|
|
||||||
try {
|
|
||||||
result = task.run()
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Iris.reportError(e)
|
|
||||||
result = ValueWithDiagnostics(
|
|
||||||
Mode.WARNING,
|
|
||||||
Diagnostic(Diagnostic.Logger.ERROR, "Error while running task ${task.id}", e)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
mode = mode.highest(result.value)
|
|
||||||
results[task] = result
|
|
||||||
context[task.id] = result.value.id
|
|
||||||
attachment[task.id] = result.diagnostics.flatMap { it.toString().split('\n') }
|
|
||||||
if (result.value != Mode.STABLE) count++
|
|
||||||
}
|
|
||||||
|
|
||||||
this.results = Collections.unmodifiableMap(results)
|
|
||||||
this.context = Collections.unmodifiableMap(context)
|
|
||||||
this.attachment = Collections.unmodifiableMap(attachment)
|
|
||||||
this.mode = mode
|
|
||||||
this.count = count
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun mode() = mode
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun asContext() = context
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun asAttachment() = attachment
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun splash() {
|
|
||||||
Iris.instance.splash()
|
|
||||||
printReports()
|
|
||||||
printFooter()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun printReports() {
|
|
||||||
when (mode) {
|
|
||||||
Mode.STABLE -> Iris.info(C.BLUE.toString() + "0 Conflicts found")
|
|
||||||
Mode.WARNING -> Iris.warn(C.GOLD.toString() + "%s Issues found", count)
|
|
||||||
Mode.UNSTABLE -> Iris.error(C.DARK_RED.toString() + "%s Issues found", count)
|
|
||||||
}
|
|
||||||
|
|
||||||
results.values.forEach { it.log(withStackTrace = true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun printFooter() {
|
|
||||||
when (mode) {
|
|
||||||
Mode.STABLE -> Iris.info(C.BLUE.toString() + "Iris is running Stable")
|
|
||||||
Mode.WARNING -> warning()
|
|
||||||
Mode.UNSTABLE -> unstable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun isForceShutdown() = forceShutdown
|
|
||||||
|
|
||||||
private fun warning() {
|
|
||||||
Iris.warn(C.GOLD.toString() + "Iris is running in Warning Mode")
|
|
||||||
Iris.warn(C.GRAY.toString() + "Some startup checks need attention. Review the messages above for tuning suggestions.")
|
|
||||||
Iris.warn(C.GRAY.toString() + "Iris will continue startup normally.")
|
|
||||||
Iris.warn("")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unstable() {
|
|
||||||
Iris.error(C.DARK_RED.toString() + "Iris is running in Danger Mode")
|
|
||||||
Iris.error("")
|
|
||||||
Iris.error(C.DARK_GRAY.toString() + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
|
||||||
Iris.error("Critical startup checks failed. Iris will continue startup in 10 seconds.")
|
|
||||||
Iris.error("Review and resolve the errors above as soon as possible.")
|
|
||||||
J.sleep(10000L)
|
|
||||||
Iris.info("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
package art.arcane.iris.core.safeguard
|
|
||||||
|
|
||||||
import art.arcane.iris.BuildConstants
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.core.IrisSettings
|
|
||||||
import art.arcane.iris.util.common.format.C
|
|
||||||
import art.arcane.volmlib.util.format.Form
|
|
||||||
import org.bukkit.Bukkit
|
|
||||||
import java.time.LocalDate
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
enum class Mode(private val color: C) {
|
|
||||||
STABLE(C.IRIS),
|
|
||||||
WARNING(C.GOLD),
|
|
||||||
UNSTABLE(C.RED);
|
|
||||||
|
|
||||||
val id = name.lowercase()
|
|
||||||
|
|
||||||
fun highest(m: Mode): Mode {
|
|
||||||
return if (m.ordinal > ordinal) m else this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun tag(subTag: String?): String {
|
|
||||||
if (subTag == null || subTag.isBlank()) return wrap("Iris") + C.GRAY + ": "
|
|
||||||
return wrap("Iris") + " " + wrap(subTag) + C.GRAY + ": "
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun wrap(tag: String?): String {
|
|
||||||
return C.BOLD.toString() + "" + C.DARK_GRAY + "[" + C.BOLD + color + tag + C.BOLD + C.DARK_GRAY + "]" + C.RESET
|
|
||||||
}
|
|
||||||
|
|
||||||
fun trySplash() {
|
|
||||||
if (!IrisSettings.get().general.isSplashLogoStartup) return
|
|
||||||
splash()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun splash() {
|
|
||||||
val padd = Form.repeat(" ", 8)
|
|
||||||
val padd2 = Form.repeat(" ", 4)
|
|
||||||
val version = Iris.instance.description.version
|
|
||||||
val releaseTrain = getReleaseTrain(version)
|
|
||||||
val serverVersion = getServerVersion()
|
|
||||||
val startupDate = getStartupDate()
|
|
||||||
val javaVersion = getJavaVersion()
|
|
||||||
|
|
||||||
val splash = arrayOf(
|
|
||||||
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
|
||||||
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + color + " .(((()))). ",
|
|
||||||
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + color + " .((((((())))))). ",
|
|
||||||
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + color + " ((((((((())))))))) " + C.GRAY + " @",
|
|
||||||
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + color + " ((((((((-))))))))) " + C.GRAY + " @@",
|
|
||||||
padd + C.GRAY + "@@@&&" + color + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
|
||||||
padd + C.GRAY + "@@" + color + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
|
||||||
padd + C.GRAY + "@" + color + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
|
||||||
padd + C.GRAY + "" + color + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
|
||||||
padd + C.GRAY + "" + color + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
|
||||||
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@",
|
|
||||||
)
|
|
||||||
|
|
||||||
val info = arrayOf(
|
|
||||||
"",
|
|
||||||
padd2 + color + " Iris, " + C.AQUA + "Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]",
|
|
||||||
padd2 + C.GRAY + " Version: " + color + version,
|
|
||||||
padd2 + C.GRAY + " By: " + color + "Volmit Software (Arcane Arts)",
|
|
||||||
padd2 + C.GRAY + " Server: " + color + serverVersion,
|
|
||||||
padd2 + C.GRAY + " Java: " + color + javaVersion + C.GRAY + " | Date: " + color + startupDate,
|
|
||||||
padd2 + C.GRAY + " Commit: " + color + BuildConstants.COMMIT + C.GRAY + "/" + color + BuildConstants.ENVIRONMENT,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
val builder = StringBuilder("\n\n")
|
|
||||||
for (i in splash.indices) {
|
|
||||||
builder.append(splash[i])
|
|
||||||
if (i < info.size) {
|
|
||||||
builder.append(info[i])
|
|
||||||
}
|
|
||||||
builder.append("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.info(builder.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getServerVersion(): String {
|
|
||||||
var version = Bukkit.getVersion()
|
|
||||||
val mcMarkerIndex = version.indexOf(" (MC:")
|
|
||||||
if (mcMarkerIndex != -1) {
|
|
||||||
version = version.substring(0, mcMarkerIndex)
|
|
||||||
}
|
|
||||||
return version
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getJavaVersion(): Int {
|
|
||||||
var version = System.getProperty("java.version")
|
|
||||||
if (version.startsWith("1.")) {
|
|
||||||
version = version.substring(2, 3)
|
|
||||||
} else {
|
|
||||||
val dot = version.indexOf(".")
|
|
||||||
if (dot != -1) {
|
|
||||||
version = version.substring(0, dot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return version.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getStartupDate(): String {
|
|
||||||
return LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getReleaseTrain(version: String): String {
|
|
||||||
var value = version
|
|
||||||
val suffixIndex = value.indexOf("-")
|
|
||||||
if (suffixIndex >= 0) {
|
|
||||||
value = value.substring(0, suffixIndex)
|
|
||||||
}
|
|
||||||
val split = value.split('.')
|
|
||||||
if (split.size >= 2) {
|
|
||||||
return split[0] + "." + split[1]
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package art.arcane.iris.core.safeguard.task
|
|
||||||
|
|
||||||
import art.arcane.iris.core.safeguard.Mode
|
|
||||||
import art.arcane.volmlib.util.format.Form
|
|
||||||
import kotlin.properties.PropertyDelegateProvider
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
|
|
||||||
abstract class Task(
|
|
||||||
val id: String,
|
|
||||||
val name: String = Form.capitalizeWords(id.replace(" ", "_").lowercase()),
|
|
||||||
) {
|
|
||||||
|
|
||||||
abstract fun run(): ValueWithDiagnostics<Mode>
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun of(id: String, name: String = id, action: () -> ValueWithDiagnostics<Mode>) = object : Task(id, name) {
|
|
||||||
override fun run() = action()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun of(id: String, action: () -> ValueWithDiagnostics<Mode>) = object : Task(id) {
|
|
||||||
override fun run() = action()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
|
||||||
ReadOnlyProperty { _, property -> of(property.name, action) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
package art.arcane.iris.core.safeguard.task
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.core.IrisWorlds
|
|
||||||
import art.arcane.iris.core.nms.INMS
|
|
||||||
import art.arcane.iris.core.nms.v1X.NMSBinding1X
|
|
||||||
import art.arcane.iris.core.safeguard.Mode
|
|
||||||
import art.arcane.iris.core.safeguard.Mode.*
|
|
||||||
import art.arcane.iris.core.safeguard.task.Diagnostic.Logger.*
|
|
||||||
import art.arcane.iris.core.safeguard.task.Task.Companion.of
|
|
||||||
import art.arcane.iris.util.project.agent.Agent
|
|
||||||
import art.arcane.iris.util.common.misc.getHardware
|
|
||||||
import org.bukkit.Bukkit
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
import kotlin.properties.PropertyDelegateProvider
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
|
|
||||||
private val memory by task {
|
|
||||||
val mem = getHardware.getProcessMemory()
|
|
||||||
when {
|
|
||||||
mem >= 3072 -> STABLE.withDiagnostics()
|
|
||||||
mem > 2048 -> STABLE.withDiagnostics(
|
|
||||||
INFO.create("Memory Recommendation"),
|
|
||||||
INFO.create("- 3GB+ process memory is recommended for Iris."),
|
|
||||||
INFO.create("- Process Memory: $mem MB")
|
|
||||||
)
|
|
||||||
else -> WARNING.withDiagnostics(
|
|
||||||
WARN.create("Low Memory"),
|
|
||||||
WARN.create("- Iris is running with 2GB or less process memory."),
|
|
||||||
WARN.create("- 3GB+ process memory is recommended for Iris."),
|
|
||||||
WARN.create("- Process Memory: $mem MB")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val incompatibilities by task {
|
|
||||||
val plugins = mutableSetOf("dynmap", "Stratos")
|
|
||||||
plugins.removeIf { server.pluginManager.getPlugin(it) == null }
|
|
||||||
|
|
||||||
if (plugins.isEmpty()) STABLE.withDiagnostics()
|
|
||||||
else {
|
|
||||||
val diagnostics = mutableListOf<Diagnostic>()
|
|
||||||
if ("dynmap" in plugins) diagnostics.addAll(
|
|
||||||
ERROR.create("Dynmap"),
|
|
||||||
ERROR.create("- The plugin Dynmap is not compatible with the server."),
|
|
||||||
ERROR.create("- If you want to have a map plugin like Dynmap, consider Bluemap.")
|
|
||||||
)
|
|
||||||
if ("Stratos" in plugins) diagnostics.addAll(
|
|
||||||
ERROR.create("Stratos"),
|
|
||||||
ERROR.create("- Iris is not compatible with other worldgen plugins.")
|
|
||||||
)
|
|
||||||
WARNING.withDiagnostics(diagnostics)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val software by task {
|
|
||||||
val supported = setOf(
|
|
||||||
"canvas",
|
|
||||||
"folia",
|
|
||||||
"purpur",
|
|
||||||
"pufferfish",
|
|
||||||
"paper",
|
|
||||||
"spigot",
|
|
||||||
"bukkit"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isCanvasServer() || supported.any { server.name.contains(it, true) }) STABLE.withDiagnostics()
|
|
||||||
else WARNING.withDiagnostics(
|
|
||||||
WARN.create("Unsupported Server Software"),
|
|
||||||
WARN.create("- Please consider using Canvas, Folia, Paper, or Purpur instead.")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val version by task {
|
|
||||||
val parts: List<String> = Iris.instance.description.version.split('-')
|
|
||||||
val supportedVersions: String = when {
|
|
||||||
parts.size >= 3 -> {
|
|
||||||
val minVersion: String = parts[1]
|
|
||||||
val maxVersion: String = parts[2]
|
|
||||||
if (minVersion == maxVersion) minVersion else "$minVersion - $maxVersion"
|
|
||||||
}
|
|
||||||
parts.size >= 2 -> parts[1]
|
|
||||||
else -> "1.21.11"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (INMS.get() !is NMSBinding1X) STABLE.withDiagnostics()
|
|
||||||
else UNSTABLE.withDiagnostics(
|
|
||||||
ERROR.create("Server Version"),
|
|
||||||
ERROR.create("- Iris only supports $supportedVersions")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val injection by task {
|
|
||||||
if (!isPaperPreferredServer() && !Agent.isInstalled()) {
|
|
||||||
WARNING.withDiagnostics(
|
|
||||||
WARN.create("Java Agent"),
|
|
||||||
WARN.create("- Skipping dynamic Java agent attach on Spigot/Bukkit to avoid runtime agent warnings."),
|
|
||||||
WARN.create("- For full runtime injection support, run with -javaagent:" + Agent.AGENT_JAR.path + " or use Canvas/Folia/Paper/Purpur.")
|
|
||||||
)
|
|
||||||
} else if (!Agent.install()) UNSTABLE.withDiagnostics(
|
|
||||||
ERROR.create("Java Agent"),
|
|
||||||
ERROR.create("- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments."),
|
|
||||||
ERROR.create("- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.path)
|
|
||||||
)
|
|
||||||
else if (!INMS.get().injectBukkit()) UNSTABLE.withDiagnostics(
|
|
||||||
ERROR.create("Code Injection"),
|
|
||||||
ERROR.create("- Failed to inject code. Please contact support")
|
|
||||||
)
|
|
||||||
else STABLE.withDiagnostics()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dimensionTypes by task {
|
|
||||||
val keys = IrisWorlds.get()
|
|
||||||
.dimensions
|
|
||||||
.map { it.dimensionTypeKey }
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
|
|
||||||
if (!INMS.get().missingDimensionTypes(*keys.toTypedArray())) STABLE.withDiagnostics()
|
|
||||||
else UNSTABLE.withDiagnostics(
|
|
||||||
ERROR.create("Dimension Types"),
|
|
||||||
ERROR.create("- Required Iris dimension types were not loaded."),
|
|
||||||
ERROR.create("- If this still happens after a restart please contact support.")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val diskSpace by task {
|
|
||||||
if (server.worldContainer.freeSpace.toDouble().div(0x4000_0000) > 3) STABLE.withDiagnostics()
|
|
||||||
else WARNING.withDiagnostics(
|
|
||||||
WARN.create("Insufficient Disk Space"),
|
|
||||||
WARN.create("- 3GB of free space is required for Iris to function.")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val java by task {
|
|
||||||
val version = Iris.getJavaVersion()
|
|
||||||
when {
|
|
||||||
version == 21 -> STABLE.withDiagnostics()
|
|
||||||
version > 21 -> STABLE.withDiagnostics(
|
|
||||||
INFO.create("Java Runtime"),
|
|
||||||
INFO.create("- Running Java $version. Iris is tested primarily on Java 21.")
|
|
||||||
)
|
|
||||||
else -> WARNING.withDiagnostics(
|
|
||||||
WARN.create("Unsupported Java version"),
|
|
||||||
WARN.create("- Java 21+ is recommended. Current runtime: Java $version")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val tasks = listOf(
|
|
||||||
memory,
|
|
||||||
incompatibilities,
|
|
||||||
software,
|
|
||||||
version,
|
|
||||||
injection,
|
|
||||||
dimensionTypes,
|
|
||||||
diskSpace,
|
|
||||||
java,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val server get() = Bukkit.getServer()
|
|
||||||
private fun isPaperPreferredServer(): Boolean {
|
|
||||||
val name = server.name.lowercase(Locale.ROOT)
|
|
||||||
return isCanvasServer()
|
|
||||||
|| name.contains("folia")
|
|
||||||
|| name.contains("paper")
|
|
||||||
|| name.contains("purpur")
|
|
||||||
|| name.contains("pufferfish")
|
|
||||||
}
|
|
||||||
private fun isCanvasServer(): Boolean {
|
|
||||||
val loader: ClassLoader? = server.javaClass.classLoader
|
|
||||||
return try {
|
|
||||||
Class.forName("io.canvasmc.canvas.region.WorldRegionizer", false, loader)
|
|
||||||
true
|
|
||||||
} catch (_: Throwable) {
|
|
||||||
server.name.contains("canvas", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun <T> MutableList<T>.addAll(vararg values: T) = values.forEach(this::add)
|
|
||||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
|
||||||
ReadOnlyProperty { _, property -> of(property.name, action) }
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package art.arcane.iris.core.safeguard.task
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.util.common.format.C
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.PrintStream
|
|
||||||
|
|
||||||
data class ValueWithDiagnostics<out T>(
|
|
||||||
val value: T,
|
|
||||||
val diagnostics: List<Diagnostic>
|
|
||||||
) {
|
|
||||||
constructor(value: T, vararg diagnostics: Diagnostic) : this(value, diagnostics.toList())
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun log(
|
|
||||||
withException: Boolean = true,
|
|
||||||
withStackTrace: Boolean = false
|
|
||||||
) {
|
|
||||||
diagnostics.forEach { it.log(withException, withStackTrace) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Diagnostic @JvmOverloads constructor(
|
|
||||||
val logger: Logger = Logger.ERROR,
|
|
||||||
val message: String,
|
|
||||||
val exception: Throwable? = null
|
|
||||||
) {
|
|
||||||
|
|
||||||
enum class Logger(
|
|
||||||
private val logger: (String) -> Unit
|
|
||||||
) {
|
|
||||||
DEBUG(Iris::debug),
|
|
||||||
RAW(Iris::msg),
|
|
||||||
INFO(Iris::info),
|
|
||||||
WARN(Iris::warn),
|
|
||||||
ERROR(Iris::error);
|
|
||||||
|
|
||||||
fun print(message: String) = message.split('\n').forEach(logger)
|
|
||||||
fun create(message: String, exception: Throwable? = null) = Diagnostic(this, message, exception)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun log(
|
|
||||||
withException: Boolean = true,
|
|
||||||
withStackTrace: Boolean = false
|
|
||||||
) {
|
|
||||||
logger.print(render(withException, withStackTrace))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun render(
|
|
||||||
withException: Boolean = true,
|
|
||||||
withStackTrace: Boolean = false
|
|
||||||
): String = buildString {
|
|
||||||
append(message)
|
|
||||||
if (withException && exception != null) {
|
|
||||||
append(": ")
|
|
||||||
append(exception)
|
|
||||||
if (withStackTrace) {
|
|
||||||
ByteArrayOutputStream().use { os ->
|
|
||||||
val ps = PrintStream(os)
|
|
||||||
exception.printStackTrace(ps)
|
|
||||||
ps.flush()
|
|
||||||
append("\n")
|
|
||||||
append(os.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = C.strip(render())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> T.withDiagnostics(vararg diagnostics: Diagnostic) = ValueWithDiagnostics(this, diagnostics.toList())
|
|
||||||
fun <T> T.withDiagnostics(diagnostics: List<Diagnostic>) = ValueWithDiagnostics(this, diagnostics)
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.func.UpdateExecutor
|
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk
|
|
||||||
import org.bukkit.Chunk
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "update.kts", compilationConfiguration = ChunkUpdateScriptDefinition::class)
|
|
||||||
abstract class ChunkUpdateScript
|
|
||||||
|
|
||||||
object ChunkUpdateScriptDefinition : ScriptCompilationConfiguration(listOf(EngineScriptDefinition), {
|
|
||||||
providedProperties(
|
|
||||||
"mantleChunk" to MantleChunk::class,
|
|
||||||
"chunk" to Chunk::class,
|
|
||||||
"executor" to UpdateExecutor::class
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = MobSpawningScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisData
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "data.kts", compilationConfiguration = DataScriptDefinition::class)
|
|
||||||
abstract class DataScript
|
|
||||||
|
|
||||||
object DataScriptDefinition : ScriptCompilationConfiguration(listOf(SimpleScriptDefinition), {
|
|
||||||
providedProperties("data" to IrisData::class)
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = DataScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.func.BiomeLookup
|
|
||||||
import art.arcane.iris.engine.IrisComplex
|
|
||||||
import art.arcane.iris.engine.framework.Engine
|
|
||||||
import art.arcane.iris.engine.`object`.IrisDimension
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "engine.kts", compilationConfiguration = EngineScriptDefinition::class)
|
|
||||||
abstract class EngineScript
|
|
||||||
|
|
||||||
object EngineScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
|
|
||||||
providedProperties(
|
|
||||||
"engine" to Engine::class,
|
|
||||||
"seed" to Long::class,
|
|
||||||
"dimension" to IrisDimension::class,
|
|
||||||
"complex" to IrisComplex::class,
|
|
||||||
"biome" to BiomeLookup::class,
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
|
|
||||||
private fun readResolve(): Any = EngineScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import org.bukkit.Location
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "spawn.kts", compilationConfiguration = MobSpawningScriptDefinition::class)
|
|
||||||
abstract class MobSpawningScript
|
|
||||||
|
|
||||||
object MobSpawningScriptDefinition : ScriptCompilationConfiguration(listOf(EngineScriptDefinition), {
|
|
||||||
providedProperties("location" to Location::class)
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = MobSpawningScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.volmlib.util.math.RNG
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "noise.kts", compilationConfiguration = NoiseScriptDefinition::class)
|
|
||||||
abstract class NoiseScript
|
|
||||||
|
|
||||||
object NoiseScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
|
|
||||||
providedProperties("rng" to RNG::class)
|
|
||||||
}) {
|
|
||||||
|
|
||||||
private fun readResolve(): Any = NoiseScriptDefinition
|
|
||||||
}
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import org.bukkit.entity.Entity
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "postspawn.kts", compilationConfiguration = PostMobSpawningScriptDefinition::class)
|
|
||||||
abstract class PostMobSpawningScript
|
|
||||||
|
|
||||||
object PostMobSpawningScriptDefinition : ScriptCompilationConfiguration(listOf(MobSpawningScriptDefinition), {
|
|
||||||
providedProperties("entity" to Entity::class)
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = PostMobSpawningScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisRegistrant
|
|
||||||
import art.arcane.iris.engine.framework.Engine
|
|
||||||
import art.arcane.iris.engine.`object`.IrisDimension
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.providedProperties
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "proc.kts", compilationConfiguration = PreprocessorScriptDefinition::class)
|
|
||||||
abstract class PreprocessorScript
|
|
||||||
|
|
||||||
object PreprocessorScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
|
|
||||||
providedProperties(
|
|
||||||
"engine" to Engine::class,
|
|
||||||
"seed" to Long::class,
|
|
||||||
"dimension" to IrisDimension::class,
|
|
||||||
"object" to IrisRegistrant::class
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = PreprocessorScriptDefinition
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.base
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.configure
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.defaultImports
|
|
||||||
import kotlin.script.experimental.dependencies.DependsOn
|
|
||||||
import kotlin.script.experimental.dependencies.Repository
|
|
||||||
import kotlin.script.experimental.jvm.dependenciesFromClassContext
|
|
||||||
import kotlin.script.experimental.jvm.jvm
|
|
||||||
|
|
||||||
@KotlinScript(fileExtension = "simple.kts", compilationConfiguration = SimpleScriptDefinition::class)
|
|
||||||
abstract class SimpleScript
|
|
||||||
|
|
||||||
object SimpleScriptDefinition : ScriptCompilationConfiguration({
|
|
||||||
defaultImports(
|
|
||||||
DependsOn::class.qualifiedName!!,
|
|
||||||
Repository::class.qualifiedName!!,
|
|
||||||
"art.arcane.iris.Iris.info",
|
|
||||||
"art.arcane.iris.Iris.debug",
|
|
||||||
"art.arcane.iris.Iris.warn",
|
|
||||||
"art.arcane.iris.Iris.error"
|
|
||||||
)
|
|
||||||
|
|
||||||
jvm {
|
|
||||||
dependenciesFromClassContext(KotlinScript::class, wholeClasspath = true)
|
|
||||||
dependenciesFromClassContext(SimpleScript::class, wholeClasspath = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
configure()
|
|
||||||
}) {
|
|
||||||
private fun readResolve(): Any = SimpleScriptDefinition
|
|
||||||
}
|
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.environment
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisRegistrant
|
|
||||||
import art.arcane.iris.core.scripting.environment.EngineEnvironment
|
|
||||||
import art.arcane.iris.core.scripting.func.BiomeLookup
|
|
||||||
import art.arcane.iris.core.scripting.func.UpdateExecutor
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.ChunkUpdateScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.EngineScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.MobSpawningScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.PostMobSpawningScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.PreprocessorScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.ScriptRunner
|
|
||||||
import art.arcane.iris.engine.framework.Engine
|
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk
|
|
||||||
import org.bukkit.Chunk
|
|
||||||
import org.bukkit.Location
|
|
||||||
import org.bukkit.entity.Entity
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class IrisExecutionEnvironment internal constructor(
|
|
||||||
private val engine: Engine,
|
|
||||||
parent: ScriptRunner?,
|
|
||||||
) : IrisPackExecutionEnvironment(engine.data, parent), EngineEnvironment {
|
|
||||||
constructor(engine: Engine) : this(engine, null)
|
|
||||||
override fun getEngine() = engine
|
|
||||||
|
|
||||||
override fun execute(script: String) =
|
|
||||||
execute(script, EngineScript::class.java, engine.parameters())
|
|
||||||
|
|
||||||
override fun evaluate(script: String) =
|
|
||||||
evaluate(script, EngineScript::class.java, engine.parameters())
|
|
||||||
|
|
||||||
override fun spawnMob(script: String, location: Location) =
|
|
||||||
evaluate(script, MobSpawningScript::class.java, engine.parameters("location" to location))
|
|
||||||
|
|
||||||
override fun postSpawnMob(script: String, location: Location, mob: Entity) =
|
|
||||||
execute(script, PostMobSpawningScript::class.java, engine.parameters("location" to location, "entity" to mob))
|
|
||||||
|
|
||||||
override fun preprocessObject(script: String, `object`: IrisRegistrant) =
|
|
||||||
execute(script, PreprocessorScript::class.java, engine.limitedParameters("object" to `object`))
|
|
||||||
|
|
||||||
override fun updateChunk(script: String, mantleChunk: MantleChunk<*>, chunk: Chunk, executor: UpdateExecutor) =
|
|
||||||
execute(script, ChunkUpdateScript::class.java, engine.parameters("mantleChunk" to mantleChunk, "chunk" to chunk, "executor" to executor))
|
|
||||||
|
|
||||||
private fun Engine.limitedParameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
|
|
||||||
return mapOf(
|
|
||||||
"data" to data,
|
|
||||||
"engine" to this,
|
|
||||||
"seed" to seedManager.seed,
|
|
||||||
"dimension" to dimension,
|
|
||||||
*values,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Engine.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
|
|
||||||
return limitedParameters(
|
|
||||||
"complex" to complex,
|
|
||||||
"biome" to BiomeLookup(::getSurfaceBiome),
|
|
||||||
*values,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.environment
|
|
||||||
|
|
||||||
import art.arcane.iris.core.loader.IrisData
|
|
||||||
import art.arcane.iris.core.scripting.environment.EngineEnvironment
|
|
||||||
import art.arcane.iris.core.scripting.environment.PackEnvironment
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.DataScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.NoiseScript
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.Script
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.ScriptRunner
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.valueOrThrow
|
|
||||||
import art.arcane.iris.engine.framework.Engine
|
|
||||||
import art.arcane.volmlib.util.math.RNG
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
open class IrisPackExecutionEnvironment internal constructor(
|
|
||||||
private val data: IrisData,
|
|
||||||
parent: ScriptRunner?
|
|
||||||
) : IrisSimpleExecutionEnvironment(data.dataFolder, parent), PackEnvironment {
|
|
||||||
constructor(data: IrisData) : this(data, null)
|
|
||||||
|
|
||||||
override fun getData() = data
|
|
||||||
|
|
||||||
override fun compile(script: String, type: KClass<*>): Script {
|
|
||||||
val loaded = data.scriptLoader.load(script)
|
|
||||||
return compileCache.get(script)
|
|
||||||
.computeIfAbsent(type) { _ -> runner.compile(type, loaded.loadFile, loaded.source) }
|
|
||||||
.valueOrThrow("Failed to compile script $script")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(script: String) =
|
|
||||||
execute(script, DataScript::class.java, data.parameters())
|
|
||||||
|
|
||||||
override fun evaluate(script: String) =
|
|
||||||
evaluate(script, DataScript::class.java, data.parameters())
|
|
||||||
|
|
||||||
override fun createNoise(script: String, rng: RNG) =
|
|
||||||
evaluate(script, NoiseScript::class.java, data.parameters("rng" to rng))
|
|
||||||
|
|
||||||
override fun with(engine: Engine) =
|
|
||||||
IrisExecutionEnvironment(engine, runner)
|
|
||||||
|
|
||||||
private fun IrisData.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
|
|
||||||
return mapOf(
|
|
||||||
"data" to this,
|
|
||||||
*values,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-187
@@ -1,187 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.environment
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.core.IrisSettings
|
|
||||||
import art.arcane.iris.core.scripting.environment.SimpleEnvironment
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.*
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.FileComponents
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.Script
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.ScriptRunner
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.classpath
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.value
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.valueOrThrow
|
|
||||||
import art.arcane.volmlib.util.collection.KMap
|
|
||||||
import art.arcane.volmlib.util.data.KCache
|
|
||||||
import art.arcane.iris.util.common.format.C
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
import kotlin.text.split
|
|
||||||
|
|
||||||
open class IrisSimpleExecutionEnvironment internal constructor(
|
|
||||||
baseDir: File,
|
|
||||||
parent: ScriptRunner?
|
|
||||||
) : SimpleEnvironment {
|
|
||||||
@JvmOverloads
|
|
||||||
constructor(baseDir: File = File(".").absoluteFile) : this(baseDir, null)
|
|
||||||
protected val compileCache = KCache<String, KMap<KClass<*>, ResultWithDiagnostics<Script>>>({ _ -> KMap() }, 1024L)
|
|
||||||
protected val runner = ScriptRunner(baseDir, parent)
|
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
script: String
|
|
||||||
) = execute(script, SimpleScript::class.java, null)
|
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
script: String,
|
|
||||||
type: Class<*>,
|
|
||||||
vars: Map<String, Any?>?
|
|
||||||
) {
|
|
||||||
Iris.debug("Execute Script (void) " + C.DARK_GREEN + script)
|
|
||||||
evaluate0(script, type.kotlin, vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun evaluate(
|
|
||||||
script: String
|
|
||||||
): Any? = evaluate(script, SimpleScript::class.java, null)
|
|
||||||
|
|
||||||
override fun evaluate(
|
|
||||||
script: String,
|
|
||||||
type: Class<*>,
|
|
||||||
vars: Map<String, Any?>?
|
|
||||||
): Any? {
|
|
||||||
Iris.debug("Execute Script (for result) " + C.DARK_GREEN + script)
|
|
||||||
return evaluate0(script, type.kotlin, vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun compile(script: String, type: KClass<*>) =
|
|
||||||
compileCache.get(script)
|
|
||||||
.computeIfAbsent(type) { _ -> runner.compile(type, script) }
|
|
||||||
.valueOrThrow("Failed to compile script")
|
|
||||||
|
|
||||||
private fun evaluate0(name: String, type: KClass<*>, properties: Map<String, Any?>? = null): Any? {
|
|
||||||
val current = Thread.currentThread()
|
|
||||||
val loader = current.contextClassLoader
|
|
||||||
current.contextClassLoader = this.javaClass.classLoader
|
|
||||||
try {
|
|
||||||
return compile(name, type)
|
|
||||||
.evaluate(properties)
|
|
||||||
.valueOrThrow("Failed to evaluate script")
|
|
||||||
.value()
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
current.contextClassLoader = loader
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun configureProject() {
|
|
||||||
runner.baseDir.mkdirs()
|
|
||||||
val libs = listOf(javaClass.classLoader.classpath, KotlinScript::class.java.classLoader.classpath)
|
|
||||||
.flatMap { it }
|
|
||||||
.sortedBy { it.absolutePath }
|
|
||||||
.toMutableList()
|
|
||||||
|
|
||||||
File(runner.baseDir, "build.gradle.kts")
|
|
||||||
.updateClasspath(libs)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val CLASSPATH = "val classpath = mapOf("
|
|
||||||
|
|
||||||
private fun File.updateClasspath(classpath: List<File>) {
|
|
||||||
val test = if (exists()) readLines() else BASE_GRADLE
|
|
||||||
writeText(test.updateClasspath(classpath))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<String>.updateClasspath(classpath: List<File>): String {
|
|
||||||
val components = linkedMapOf<String, FileComponents>()
|
|
||||||
classpath.forEach {
|
|
||||||
val parts = it.canonicalPath.split(File.separatorChar)
|
|
||||||
if (parts.size <= 1) {
|
|
||||||
Iris.error("Invalid classpath entry: $it")
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
|
|
||||||
var parent = components.computeIfAbsent(parts[0]) { FileComponents(parts[0], true) }
|
|
||||||
for (part in parts.subList(1, parts.size)) {
|
|
||||||
parent = parent.append(part)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mapped = components.values.associate {
|
|
||||||
var current = it
|
|
||||||
val root = buildString {
|
|
||||||
while (current.children.size == 1) {
|
|
||||||
append(current.segment)
|
|
||||||
append(File.separatorChar)
|
|
||||||
current = current.children.first()
|
|
||||||
}
|
|
||||||
append(current.segment)
|
|
||||||
append(File.separatorChar)
|
|
||||||
}.escapedPath
|
|
||||||
|
|
||||||
val result = mutableSetOf<String>()
|
|
||||||
val queue = ArrayDeque<Pair<String?, Collection<FileComponents>>>()
|
|
||||||
queue.add(null to current.children)
|
|
||||||
while (queue.isNotEmpty()) {
|
|
||||||
val pair = queue.removeFirst()
|
|
||||||
val path = pair.first?.let { p -> p + File.separatorChar } ?: ""
|
|
||||||
pair.second.forEach { child ->
|
|
||||||
val path = path + child.segment
|
|
||||||
if (child.children.isEmpty()) result.add(path.escapedPath)
|
|
||||||
else queue.add(path to child.children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root to result
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val classpath = mapped.entries.joinToString(",", CLASSPATH, ")") {
|
|
||||||
"\"${it.key}\" to setOf(${it.value.joinToString(", ") { f -> "\"$f\"" }})"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val mod = toMutableList()
|
|
||||||
val index = indexOfFirst { it.startsWith(CLASSPATH) }
|
|
||||||
if (index == -1) {
|
|
||||||
mod.clear()
|
|
||||||
mod.addAll(BASE_GRADLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
mod[if (index == -1) 0 else index] = classpath
|
|
||||||
return mod.joinToString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val String.escapedPath
|
|
||||||
get() = replace("\\", "\\\\").replace("\"", "\\\"")
|
|
||||||
|
|
||||||
private const val ARTIFACT_ID = $$"local:${it.substringBeforeLast(\".jar\")}:1.0.0"
|
|
||||||
private val BASE_GRADLE = """
|
|
||||||
val classpath = mapOf()
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm") version("2.2.0")
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
flatDir {
|
|
||||||
dirs(classpath.keys)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val script by configurations.creating
|
|
||||||
configurations.compileOnly { extendsFrom(script) }
|
|
||||||
configurations.kotlinScriptDef { extendsFrom(script) }
|
|
||||||
configurations.kotlinScriptDefExtensions { extendsFrom(script) }
|
|
||||||
configurations.kotlinCompilerClasspath { extendsFrom(script) }
|
|
||||||
configurations.kotlinCompilerPluginClasspath { extendsFrom(script) }
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath.values.flatMap { it }.forEach { script("$ARTIFACT_ID") }
|
|
||||||
}""".trimIndent().split("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.script.experimental.api.*
|
|
||||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
|
||||||
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
|
|
||||||
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
|
||||||
|
|
||||||
data class CachedScript(
|
|
||||||
private val base: CompiledScript,
|
|
||||||
private val host: BasicJvmScriptingHost,
|
|
||||||
private val hostConfig: ScriptingHostConfiguration
|
|
||||||
) : Script, CompiledScript {
|
|
||||||
private val scripts = base.otherScripts.map { CachedScript(it, host, hostConfig) }
|
|
||||||
private val evalConfig = createEvaluationConfiguration()
|
|
||||||
private val lock = ReentrantLock()
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var value: ResultWithDiagnostics<KClass<*>>? = null
|
|
||||||
|
|
||||||
override val otherScripts: List<CompiledScript>
|
|
||||||
get() = scripts
|
|
||||||
|
|
||||||
override val sourceLocationId: String?
|
|
||||||
get() = base.sourceLocationId
|
|
||||||
|
|
||||||
override val compilationConfiguration: ScriptCompilationConfiguration
|
|
||||||
get() = base.compilationConfiguration
|
|
||||||
|
|
||||||
override val resultField: Pair<String, KotlinType>?
|
|
||||||
get() = base.resultField
|
|
||||||
|
|
||||||
|
|
||||||
override suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration?) = value ?: run {
|
|
||||||
lock.lock()
|
|
||||||
try {
|
|
||||||
value ?: base.getClass(scriptEvaluationConfiguration).also { value = it }
|
|
||||||
} finally {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun evaluate(properties: Map<String, Any?>?) = host.runInCoroutineContext {
|
|
||||||
host.evaluator(this, createEvaluationConfiguration(properties))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createEvaluationConfiguration(properties: Map<String, Any?>?): ScriptEvaluationConfiguration {
|
|
||||||
if (properties == null || properties.isEmpty())
|
|
||||||
return evalConfig
|
|
||||||
|
|
||||||
return evalConfig.with {
|
|
||||||
providedProperties(properties)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createEvaluationConfiguration(): ScriptEvaluationConfiguration {
|
|
||||||
val type = compilationConfiguration[ScriptCompilationConfiguration.baseClass]?.fromClass!!
|
|
||||||
return createEvaluationConfigurationFromTemplate(
|
|
||||||
KotlinType(type),
|
|
||||||
hostConfig,
|
|
||||||
type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner
|
|
||||||
|
|
||||||
import kotlin.script.experimental.api.EvaluationResult
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
|
|
||||||
interface Script {
|
|
||||||
fun evaluate(properties: Map<String, Any?>?): ResultWithDiagnostics<EvaluationResult>
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.base.SimpleScript
|
|
||||||
import java.io.File
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
|
||||||
import kotlin.script.experimental.api.KotlinType
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
|
||||||
import kotlin.script.experimental.api.SourceCode
|
|
||||||
import kotlin.script.experimental.host.FileScriptSource
|
|
||||||
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
|
||||||
import kotlin.script.experimental.host.toScriptSource
|
|
||||||
import kotlin.script.experimental.host.withDefaultsFrom
|
|
||||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
|
||||||
import kotlin.script.experimental.jvm.dependenciesFromClassContext
|
|
||||||
import kotlin.script.experimental.jvm.jvm
|
|
||||||
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
|
||||||
|
|
||||||
class ScriptRunner(
|
|
||||||
val baseDir: File,
|
|
||||||
parent: ScriptRunner? = null,
|
|
||||||
private val host: BasicJvmScriptingHost = BasicJvmScriptingHost()
|
|
||||||
) {
|
|
||||||
private val configs = ConcurrentHashMap<KClass<*>, ScriptCompilationConfiguration>()
|
|
||||||
private val hostConfig = host.baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
|
|
||||||
private val sharedClassLoader: SharedClassLoader = parent?.sharedClassLoader ?: SharedClassLoader()
|
|
||||||
private val resolver = createResolver(baseDir)
|
|
||||||
|
|
||||||
fun compile(type: KClass<*>, raw: String, name: String? = null) = compile(type, raw.toScriptSource(name))
|
|
||||||
fun compile(type: KClass<*>, file: File, preloaded: String? = null) = compile(type, FileScriptSource(file, preloaded))
|
|
||||||
|
|
||||||
private fun compile(
|
|
||||||
type: KClass<*>,
|
|
||||||
code: SourceCode
|
|
||||||
): ResultWithDiagnostics<Script> = host.runInCoroutineContext {
|
|
||||||
host.compiler(code, configs.computeIfAbsent(type, ::createConfig))
|
|
||||||
.map { CachedScript(it, host, hostConfig) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createConfig(type: KClass<*>) = createCompilationConfigurationFromTemplate(
|
|
||||||
KotlinType(type),
|
|
||||||
hostConfig,
|
|
||||||
type
|
|
||||||
) {
|
|
||||||
dependencyResolver(resolver)
|
|
||||||
packDirectory(baseDir)
|
|
||||||
sharedClassloader(sharedClassLoader)
|
|
||||||
server(true)
|
|
||||||
|
|
||||||
if (SimpleScript::class.java.isAssignableFrom(type.java))
|
|
||||||
return@createCompilationConfigurationFromTemplate
|
|
||||||
|
|
||||||
jvm {
|
|
||||||
dependenciesFromClassContext(type, wholeClasspath = true)
|
|
||||||
dependenciesFromClassContext(this::class, wholeClasspath = true)
|
|
||||||
dependenciesFromClassContext(KotlinScript::class, wholeClasspath = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
configure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner
|
|
||||||
|
|
||||||
import art.arcane.iris.core.scripting.kotlin.runner.resolver.CompoundDependenciesResolver
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import kotlin.script.experimental.api.*
|
|
||||||
import kotlin.script.experimental.dependencies.DependsOn
|
|
||||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.Repository
|
|
||||||
import kotlin.script.experimental.dependencies.addRepository
|
|
||||||
import kotlin.script.experimental.dependencies.impl.SimpleExternalDependenciesResolverOptionsParser
|
|
||||||
import kotlin.script.experimental.jvm.JvmDependency
|
|
||||||
import kotlin.script.experimental.jvm.updateClasspath
|
|
||||||
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
|
||||||
import kotlin.script.experimental.util.PropertiesCollection
|
|
||||||
import kotlin.script.experimental.util.filterByAnnotationType
|
|
||||||
|
|
||||||
internal fun <T, R> ResultWithDiagnostics<T>.map(transformer: (T) -> R): ResultWithDiagnostics<R> = when (this) {
|
|
||||||
is ResultWithDiagnostics.Success -> ResultWithDiagnostics.Success(transformer(value), reports)
|
|
||||||
is ResultWithDiagnostics.Failure -> this
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun EvaluationResult.value() = returnValue.value()
|
|
||||||
internal fun ResultValue.value(): Any? =
|
|
||||||
when (this) {
|
|
||||||
is ResultValue.Value -> value
|
|
||||||
is ResultValue.Error -> throw error
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class FileComponents(
|
|
||||||
val segment: String,
|
|
||||||
val root: Boolean = false,
|
|
||||||
) {
|
|
||||||
private val children0 = mutableMapOf<String, FileComponents>()
|
|
||||||
val children get() = children0.values
|
|
||||||
|
|
||||||
fun append(segment: String): FileComponents =
|
|
||||||
children0.computeIfAbsent(segment) { FileComponents(segment) }
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = segment.hashCode()
|
|
||||||
result = 31 * result + children0.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is FileComponents) return false
|
|
||||||
|
|
||||||
if (segment != other.segment) return false
|
|
||||||
if (children0 != other.children0) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val workDir = File(".").normalize()
|
|
||||||
internal fun createResolver(baseDir: File = workDir) = CompoundDependenciesResolver(baseDir)
|
|
||||||
|
|
||||||
private val resolver = createResolver()
|
|
||||||
private val loader = SharedClassLoader()
|
|
||||||
|
|
||||||
private fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> = runCatching {
|
|
||||||
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
|
||||||
?: return context.compilationConfiguration.asSuccess()
|
|
||||||
|
|
||||||
val reports = mutableListOf<ScriptDiagnostic>()
|
|
||||||
val loader = context.compilationConfiguration[ScriptCompilationConfiguration.sharedClassloader]
|
|
||||||
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
|
|
||||||
val server = context.compilationConfiguration[ScriptCompilationConfiguration.server] ?: false
|
|
||||||
context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory]
|
|
||||||
?.addPack(resolver)
|
|
||||||
?: context.script.locationId
|
|
||||||
?.let(::File)
|
|
||||||
?.takeIf { it.exists() }
|
|
||||||
?.run {
|
|
||||||
val location = SourceCode.LocationWithId(context.script.locationId!!, SourceCode.Location(SourceCode.Position(0, 0)))
|
|
||||||
val parts = normalize().absolutePath.split(File.separatorChar)
|
|
||||||
for (i in parts.size - 1 downTo 1) {
|
|
||||||
if (parts[i] != "scripts") continue
|
|
||||||
val pack = File(parts.subList(0, i).joinToString(File.separator))
|
|
||||||
if (!File(pack, "dimensions${File.separator}${parts[i - 1]}.json").exists())
|
|
||||||
continue
|
|
||||||
pack.addPack(resolver)
|
|
||||||
reports.add(ScriptDiagnostic(
|
|
||||||
ScriptDiagnostic.unspecifiedInfo,
|
|
||||||
"Adding pack \"$pack\"",
|
|
||||||
ScriptDiagnostic.Severity.INFO,
|
|
||||||
location
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return runBlocking {
|
|
||||||
resolver.resolveDependencies(annotations, server)
|
|
||||||
}.onSuccess { classpath ->
|
|
||||||
context.compilationConfiguration.with {
|
|
||||||
if (!server) {
|
|
||||||
updateClasspath(classpath.map { it.first })
|
|
||||||
return@with
|
|
||||||
}
|
|
||||||
|
|
||||||
val newClasspath = classpath.filterNewClasspath(this[ScriptCompilationConfiguration.dependencies])
|
|
||||||
?: return@with
|
|
||||||
val shared = classpath.mapNotNull { p -> p.first.takeIf { p.second } }
|
|
||||||
if (shared.isNotEmpty()) loader!!.addFiles(shared)
|
|
||||||
|
|
||||||
val regular = newClasspath
|
|
||||||
.map { p -> p.first }
|
|
||||||
.let { JvmDependency(it) }
|
|
||||||
ScriptCompilationConfiguration.dependencies.append(regular)
|
|
||||||
}.asSuccess()
|
|
||||||
}.appendReports(reports)
|
|
||||||
}.getOrElse { ResultWithDiagnostics.Failure(it.asDiagnostics()) }
|
|
||||||
|
|
||||||
private fun Collection<Pair<File, Boolean>>.filterNewClasspath(known: Collection<ScriptDependency>?): List<Pair<File, Boolean>>? {
|
|
||||||
if (isEmpty()) return null
|
|
||||||
|
|
||||||
val knownClasspath = known?.flatMapTo(hashSetOf()) {
|
|
||||||
(it as? JvmDependency)?.classpath ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return filterNot { knownClasspath?.contains(it.first) == true }.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun ExternalDependenciesResolver.resolveDependencies(
|
|
||||||
annotations: Iterable<ScriptSourceAnnotation<*>>,
|
|
||||||
server: Boolean
|
|
||||||
): ResultWithDiagnostics<List<Pair<File, Boolean>>> {
|
|
||||||
val reports = mutableListOf<ScriptDiagnostic>()
|
|
||||||
annotations.forEach { (annotation, locationWithId) ->
|
|
||||||
when (annotation) {
|
|
||||||
is Repository -> {
|
|
||||||
val options = SimpleExternalDependenciesResolverOptionsParser(*annotation.options, locationWithId = locationWithId)
|
|
||||||
.valueOr { return it }
|
|
||||||
|
|
||||||
for (coordinates in annotation.repositoriesCoordinates) {
|
|
||||||
val added = addRepository(coordinates, options, locationWithId)
|
|
||||||
.also { reports.addAll(it.reports) }
|
|
||||||
.valueOr { return it }
|
|
||||||
|
|
||||||
if (!added)
|
|
||||||
return reports + makeFailureResult(
|
|
||||||
"Unrecognized repository coordinates: $coordinates",
|
|
||||||
locationWithId = locationWithId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is DependsOn -> {}
|
|
||||||
else -> return reports + makeFailureResult("Unknown annotation ${annotation.javaClass}", locationWithId = locationWithId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reports + annotations.filterByAnnotationType<DependsOn>()
|
|
||||||
.flatMapSuccess { (annotation, locationWithId) ->
|
|
||||||
SimpleExternalDependenciesResolverOptionsParser(
|
|
||||||
*annotation.options,
|
|
||||||
locationWithId = locationWithId
|
|
||||||
).onSuccess { options ->
|
|
||||||
if (!server && true == options.server) {
|
|
||||||
return@onSuccess listOf<Pair<File, Boolean>>().asSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
annotation.artifactsCoordinates.asIterable().flatMapSuccess { artifactCoordinates ->
|
|
||||||
resolve(artifactCoordinates, options, locationWithId)
|
|
||||||
}.map { files -> files.map { it to (options.shared ?: false) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val ExternalDependenciesResolver.Options.shared get() = flag("shared")
|
|
||||||
private val ExternalDependenciesResolver.Options.server get() = flag("server")
|
|
||||||
internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList()
|
|
||||||
|
|
||||||
internal fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R = valueOr {
|
|
||||||
throw RuntimeException(it.reports.joinToString("\n", "$message\n") { r -> r.render(withStackTrace = true) })
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver, true)
|
|
||||||
internal val ScriptCompilationConfigurationKeys.packDirectory by PropertiesCollection.key<File>(null, true)
|
|
||||||
internal val ScriptCompilationConfigurationKeys.sharedClassloader by PropertiesCollection.key<SharedClassLoader>(null, true)
|
|
||||||
internal val ScriptCompilationConfigurationKeys.server by PropertiesCollection.key(false, isTransient = true)
|
|
||||||
|
|
||||||
private fun File.addPack(resolver: CompoundDependenciesResolver) = resolver.addPack(this)
|
|
||||||
private fun <R> ResultWithDiagnostics<R>.appendReports(reports : Collection<ScriptDiagnostic>) =
|
|
||||||
if (reports.isEmpty()) this
|
|
||||||
else when (this) {
|
|
||||||
is ResultWithDiagnostics.Success -> ResultWithDiagnostics.Success(value, this.reports + reports)
|
|
||||||
is ResultWithDiagnostics.Failure -> ResultWithDiagnostics.Failure(this.reports + reports)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class SharedClassLoader(parent: ClassLoader = SharedClassLoader::class.java.classLoader) : URLClassLoader(arrayOf(), parent) {
|
|
||||||
val dependency get() = JvmDependency(classpath)
|
|
||||||
|
|
||||||
fun addFiles(files: List<File>) {
|
|
||||||
files.forEach { addURL(it.toURI().toURL()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun ScriptCompilationConfiguration.Builder.configure() {
|
|
||||||
refineConfiguration {
|
|
||||||
beforeParsing { context -> try {
|
|
||||||
context.compilationConfiguration.with {
|
|
||||||
if (context.compilationConfiguration[ScriptCompilationConfiguration.server] ?: false) {
|
|
||||||
val sharedClasspath = this[ScriptCompilationConfiguration.sharedClassloader]!!.classpath
|
|
||||||
if (sharedClasspath.isNotEmpty()) {
|
|
||||||
ScriptCompilationConfiguration.dependencies.append(JvmDependency(sharedClasspath))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.asSuccess()
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
ResultWithDiagnostics.Failure(e.asDiagnostics())
|
|
||||||
}}
|
|
||||||
|
|
||||||
onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-118
@@ -1,118 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner.resolver
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.collections.component1
|
|
||||||
import kotlin.collections.component2
|
|
||||||
import kotlin.collections.iterator
|
|
||||||
import kotlin.collections.set
|
|
||||||
import kotlin.script.experimental.api.IterableResultsCollector
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
|
||||||
import kotlin.script.experimental.api.SourceCode
|
|
||||||
import kotlin.script.experimental.api.asErrorDiagnostics
|
|
||||||
import kotlin.script.experimental.api.asSuccess
|
|
||||||
import kotlin.script.experimental.dependencies.ArtifactWithLocation
|
|
||||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
|
||||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
|
||||||
|
|
||||||
class CompoundDependenciesResolver(
|
|
||||||
baseDir: File
|
|
||||||
) : DependenciesResolver {
|
|
||||||
private val resolvers = listOf(FileDependenciesResolver(baseDir), LocalMavenDependenciesResolver())
|
|
||||||
|
|
||||||
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean {
|
|
||||||
return resolvers.any { it.acceptsRepository(repositoryCoordinates) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun acceptsArtifact(artifactCoordinates: String): Boolean {
|
|
||||||
return resolvers.any { it.acceptsArtifact(artifactCoordinates) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addRepository(
|
|
||||||
repositoryCoordinates: RepositoryCoordinates,
|
|
||||||
options: ExternalDependenciesResolver.Options,
|
|
||||||
sourceCodeLocation: SourceCode.LocationWithId?
|
|
||||||
): ResultWithDiagnostics<Boolean> {
|
|
||||||
var success = false
|
|
||||||
var repositoryAdded = false
|
|
||||||
val reports = mutableListOf<ScriptDiagnostic>()
|
|
||||||
|
|
||||||
for (resolver in resolvers) {
|
|
||||||
when (val result = resolver.addRepository(repositoryCoordinates, options, sourceCodeLocation)) {
|
|
||||||
is ResultWithDiagnostics.Success -> {
|
|
||||||
success = true
|
|
||||||
repositoryAdded = repositoryAdded || result.value
|
|
||||||
reports.addAll(result.reports)
|
|
||||||
}
|
|
||||||
is ResultWithDiagnostics.Failure -> reports.addAll(result.reports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return when {
|
|
||||||
success -> repositoryAdded.asSuccess(reports)
|
|
||||||
reports.isEmpty() -> makeResolveFailureResult(
|
|
||||||
"No dependency resolver found that recognizes the repository coordinates '$repositoryCoordinates'",
|
|
||||||
sourceCodeLocation
|
|
||||||
)
|
|
||||||
else -> ResultWithDiagnostics.Failure(reports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun resolve(
|
|
||||||
artifactsWithLocations: List<ArtifactWithLocation>,
|
|
||||||
options: ExternalDependenciesResolver.Options
|
|
||||||
): ResultWithDiagnostics<List<File>> {
|
|
||||||
val resultsCollector = IterableResultsCollector<File>()
|
|
||||||
|
|
||||||
val artifactToResolverIndex = mutableMapOf<ArtifactWithLocation, Int>().apply {
|
|
||||||
for (artifactWithLocation in artifactsWithLocations) {
|
|
||||||
put(artifactWithLocation, -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (artifactToResolverIndex.isNotEmpty()) {
|
|
||||||
val resolverGroups = mutableMapOf<Int, MutableList<ArtifactWithLocation>>()
|
|
||||||
|
|
||||||
for ((artifactWithLocation, resolverIndex) in artifactToResolverIndex) {
|
|
||||||
val (artifact, sourceCodeLocation) = artifactWithLocation
|
|
||||||
|
|
||||||
var currentIndex = resolverIndex + 1
|
|
||||||
while (currentIndex < resolvers.size) {
|
|
||||||
if (resolvers[currentIndex].acceptsArtifact(artifact)) break
|
|
||||||
++currentIndex
|
|
||||||
}
|
|
||||||
if (currentIndex == resolvers.size) {
|
|
||||||
if (resolverIndex == -1) {
|
|
||||||
resultsCollector.addDiagnostic(
|
|
||||||
"No suitable dependency resolver found for artifact '$artifact'"
|
|
||||||
.asErrorDiagnostics(locationWithId = sourceCodeLocation)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolverGroups
|
|
||||||
.getOrPut(currentIndex) { mutableListOf() }
|
|
||||||
.add(artifactWithLocation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
artifactToResolverIndex.clear()
|
|
||||||
for ((resolverIndex, artifacts) in resolverGroups) {
|
|
||||||
val resolver = resolvers[resolverIndex]
|
|
||||||
val resolveResult = resolver.resolve(artifacts, options)
|
|
||||||
resultsCollector.add(resolveResult)
|
|
||||||
if (resolveResult.reports.isNotEmpty()) {
|
|
||||||
for (artifact in artifacts) {
|
|
||||||
artifactToResolverIndex[artifact] = resolverIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultsCollector.getResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addPack(directory: File) {
|
|
||||||
resolvers.forEach { it.addPack(directory) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner.resolver
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|
||||||
|
|
||||||
interface DependenciesResolver : ExternalDependenciesResolver {
|
|
||||||
fun addPack(directory: File)
|
|
||||||
}
|
|
||||||
-69
@@ -1,69 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner.resolver
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
import kotlin.script.experimental.api.SourceCode
|
|
||||||
import kotlin.script.experimental.api.asSuccess
|
|
||||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
|
||||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
|
||||||
import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
|
|
||||||
|
|
||||||
class FileDependenciesResolver(
|
|
||||||
private val baseDir: File,
|
|
||||||
) : DependenciesResolver {
|
|
||||||
private val localRepos = ConcurrentHashMap.newKeySet<File>(1).also { it.add(baseDir) }
|
|
||||||
|
|
||||||
private fun String.toRepositoryFileOrNull(): File? =
|
|
||||||
File(baseDir, this).takeIf { it.exists() && it.isDirectory }
|
|
||||||
|
|
||||||
private fun RepositoryCoordinates.toFilePath() =
|
|
||||||
(this.toRepositoryUrlOrNull()?.takeIf { it.protocol == "file" }?.path ?: string).toRepositoryFileOrNull()
|
|
||||||
|
|
||||||
override fun addRepository(
|
|
||||||
repositoryCoordinates: RepositoryCoordinates,
|
|
||||||
options: ExternalDependenciesResolver.Options,
|
|
||||||
sourceCodeLocation: SourceCode.LocationWithId?
|
|
||||||
): ResultWithDiagnostics<Boolean> {
|
|
||||||
if (!acceptsRepository(repositoryCoordinates)) return false.asSuccess()
|
|
||||||
|
|
||||||
val repoDir = repositoryCoordinates.toFilePath()
|
|
||||||
?: return makeResolveFailureResult("Invalid repository location: '${repositoryCoordinates}'", sourceCodeLocation)
|
|
||||||
|
|
||||||
localRepos.add(repoDir)
|
|
||||||
|
|
||||||
return true.asSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun resolve(
|
|
||||||
artifactCoordinates: String,
|
|
||||||
options: ExternalDependenciesResolver.Options,
|
|
||||||
sourceCodeLocation: SourceCode.LocationWithId?
|
|
||||||
): ResultWithDiagnostics<List<File>> {
|
|
||||||
if (!acceptsArtifact(artifactCoordinates)) throw IllegalArgumentException("Path is invalid")
|
|
||||||
|
|
||||||
val messages = mutableListOf<String>()
|
|
||||||
|
|
||||||
for (repo in localRepos) {
|
|
||||||
// TODO: add coordinates and wildcard matching
|
|
||||||
val file = File(repo, artifactCoordinates)
|
|
||||||
when {
|
|
||||||
!file.exists() -> messages.add("File '$file' not found")
|
|
||||||
!file.isFile && !file.isDirectory -> messages.add("Path '$file' is neither file nor directory")
|
|
||||||
else -> return ResultWithDiagnostics.Success(listOf(file))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return makeResolveFailureResult(messages.joinToString("; "), sourceCodeLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun acceptsArtifact(artifactCoordinates: String) =
|
|
||||||
!artifactCoordinates.isBlank() // TODO: make check stronger, e.g. using NIO's Path
|
|
||||||
|
|
||||||
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean = repositoryCoordinates.toFilePath() != null
|
|
||||||
|
|
||||||
override fun addPack(directory: File) {
|
|
||||||
localRepos.add(directory)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
package art.arcane.iris.core.scripting.kotlin.runner.resolver
|
|
||||||
|
|
||||||
import art.arcane.volmlib.util.io.IO
|
|
||||||
import org.dom4j.Document
|
|
||||||
import org.dom4j.DocumentFactory
|
|
||||||
import org.dom4j.io.SAXReader
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
|
||||||
import kotlin.script.experimental.api.SourceCode
|
|
||||||
import kotlin.script.experimental.dependencies.ArtifactWithLocation
|
|
||||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
|
||||||
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
|
||||||
|
|
||||||
class LocalMavenDependenciesResolver : DependenciesResolver {
|
|
||||||
private lateinit var localRepo: File
|
|
||||||
private val maven = MavenDependenciesResolver(true)
|
|
||||||
|
|
||||||
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates) = maven.acceptsRepository(repositoryCoordinates)
|
|
||||||
override fun acceptsArtifact(artifactCoordinates: String) = maven.acceptsArtifact(artifactCoordinates)
|
|
||||||
|
|
||||||
override fun addRepository(
|
|
||||||
repositoryCoordinates: RepositoryCoordinates,
|
|
||||||
options: ExternalDependenciesResolver.Options,
|
|
||||||
sourceCodeLocation: SourceCode.LocationWithId?
|
|
||||||
) = maven.addRepository(repositoryCoordinates, options, sourceCodeLocation)
|
|
||||||
|
|
||||||
override suspend fun resolve(
|
|
||||||
artifactsWithLocations: List<ArtifactWithLocation>,
|
|
||||||
options: ExternalDependenciesResolver.Options
|
|
||||||
): ResultWithDiagnostics<List<File>> {
|
|
||||||
val userOld: String? = System.getProperty("org.apache.maven.user-settings")
|
|
||||||
val globalOld: String? = System.getProperty("org.apache.maven.global-settings")
|
|
||||||
|
|
||||||
try {
|
|
||||||
System.setProperty("org.apache.maven.user-settings", createSettings(userOld))
|
|
||||||
System.clearProperty("org.apache.maven.global-settings")
|
|
||||||
|
|
||||||
return maven.resolve(artifactsWithLocations, options)
|
|
||||||
} finally {
|
|
||||||
setProperty("org.apache.maven.user-settings", userOld)
|
|
||||||
setProperty("org.apache.maven.global-settings", globalOld)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createSettings(user: String?): String {
|
|
||||||
val settingsFile = File(localRepo, "settings.xml")
|
|
||||||
val document = readSettings(user)
|
|
||||||
val node = document.selectSingleNode("//localRepository")
|
|
||||||
?: document.rootElement.addElement("localRepository")
|
|
||||||
|
|
||||||
if (node.text != localRepo.absolutePath) {
|
|
||||||
node.text = localRepo.absolutePath
|
|
||||||
|
|
||||||
IO.write(settingsFile, document)
|
|
||||||
}
|
|
||||||
return settingsFile.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readSettings(user: String?): Document {
|
|
||||||
val baseFile = user?.let(::File)?.takeIf { it.exists() } ?: File(
|
|
||||||
System.getProperty("user.home"),
|
|
||||||
".m2/settings.xml"
|
|
||||||
).takeIf { it.exists() }?.let { return SAXReader().read(it) }
|
|
||||||
return if (baseFile != null) SAXReader().read(baseFile) else DocumentFactory.getInstance().createDocument().also {
|
|
||||||
it.addElement("settings")
|
|
||||||
.addAttribute("xmlns", "http://maven.apache.org/SETTINGS/1.0.0")
|
|
||||||
.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
|
||||||
.addAttribute("xsi:schemaLocation", "http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setProperty(name: String, value: String?) {
|
|
||||||
when(value) {
|
|
||||||
null -> System.clearProperty(name)
|
|
||||||
else -> System.setProperty(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addPack(directory: File) {
|
|
||||||
if (!::localRepo.isInitialized) {
|
|
||||||
localRepo = directory.resolve(".iris/m2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
package art.arcane.iris.engine.mantle
|
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
|
||||||
import art.arcane.iris.core.IrisSettings
|
|
||||||
import art.arcane.iris.core.nms.container.Pair
|
|
||||||
import art.arcane.iris.engine.framework.Engine
|
|
||||||
import art.arcane.iris.util.project.context.ChunkContext
|
|
||||||
import art.arcane.iris.util.common.misc.RegenRuntime
|
|
||||||
import art.arcane.volmlib.util.matter.Matter
|
|
||||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates
|
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle
|
|
||||||
import art.arcane.volmlib.util.mantle.flag.MantleFlag
|
|
||||||
import art.arcane.iris.util.common.parallel.MultiBurst
|
|
||||||
import art.arcane.iris.util.project.matter.TileWrapper
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.bukkit.block.data.BlockData
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
interface MatterGenerator {
|
|
||||||
val engine: Engine
|
|
||||||
val mantle: Mantle<Matter>
|
|
||||||
val radius: Int
|
|
||||||
val realRadius: Int
|
|
||||||
val components: List<Pair<List<MantleComponent>, Int>>
|
|
||||||
|
|
||||||
@ChunkCoordinates
|
|
||||||
fun generateMatter(x: Int, z: Int, multicore: Boolean, context: ChunkContext) {
|
|
||||||
if (!engine.dimension.isUseMantle) return
|
|
||||||
val multicore = multicore || IrisSettings.get().generator.isUseMulticoreMantle
|
|
||||||
val threadName = Thread.currentThread().name
|
|
||||||
val regenThread = threadName.startsWith("Iris-Regen-")
|
|
||||||
val traceRegen = regenThread && IrisSettings.get().general.isDebug
|
|
||||||
val forceRegen = regenThread
|
|
||||||
val regenPassKey = if (forceRegen) resolveRegenPassKey(threadName) else null
|
|
||||||
val optimizedRegen = forceRegen && !IrisSettings.get().general.isDebug && regenPassKey != null
|
|
||||||
val writeRadius = if (optimizedRegen) min(radius, realRadius) else radius
|
|
||||||
val clearedChunks = if (optimizedRegen) getRegenPassSet(regenClearedChunksByPass, regenPassKey!!) else HashSet<Long>()
|
|
||||||
val plannedChunks = if (optimizedRegen) getRegenPassSet(regenPlannedChunksByPass, regenPassKey!!) else null
|
|
||||||
|
|
||||||
if (optimizedRegen) {
|
|
||||||
touchRegenPass(regenPassKey!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceRegen) {
|
|
||||||
Iris.info("Regen matter start: center=$x,$z radius=$radius realRadius=$realRadius writeRadius=$writeRadius multicore=$multicore components=${components.size} optimized=$optimizedRegen passKey=${regenPassKey ?: "none"} thread=$threadName")
|
|
||||||
}
|
|
||||||
|
|
||||||
MantleWriter(engine.mantle, mantle, x, z, writeRadius, multicore).use { writer ->
|
|
||||||
for (pair in components) {
|
|
||||||
val rawPassRadius = pair.b
|
|
||||||
val passRadius = if (optimizedRegen) min(rawPassRadius, writeRadius) else rawPassRadius
|
|
||||||
val passFlags = pair.a.joinToString(",") { it.flag.toString() }
|
|
||||||
val passFlagKey = if (optimizedRegen) "$regenPassKey|$passFlags" else null
|
|
||||||
val generatedChunks = if (passFlagKey != null) getRegenPassSet(regenGeneratedChunksByPass, passFlagKey) else null
|
|
||||||
var visitedChunks = 0
|
|
||||||
var clearedCount = 0
|
|
||||||
var plannedSkipped = 0
|
|
||||||
var componentSkipped = 0
|
|
||||||
var componentForcedReset = 0
|
|
||||||
var launchedLayers = 0
|
|
||||||
var dedupSkipped = 0
|
|
||||||
|
|
||||||
if (passFlagKey != null) {
|
|
||||||
touchRegenPass(passFlagKey)
|
|
||||||
}
|
|
||||||
if (traceRegen) {
|
|
||||||
Iris.info("Regen matter pass start: center=$x,$z passRadius=$passRadius rawPassRadius=$rawPassRadius flags=[$passFlags]")
|
|
||||||
}
|
|
||||||
|
|
||||||
runBlocking {
|
|
||||||
radius(x, z, passRadius) { passX, passZ ->
|
|
||||||
visitedChunks++
|
|
||||||
val passKey = chunkKey(passX, passZ)
|
|
||||||
if (generatedChunks != null && !generatedChunks.add(passKey)) {
|
|
||||||
dedupSkipped++
|
|
||||||
return@radius
|
|
||||||
}
|
|
||||||
|
|
||||||
val mc = writer.acquireChunk(passX, passZ)
|
|
||||||
if (forceRegen) {
|
|
||||||
if (clearedChunks.add(passKey)) {
|
|
||||||
mc.deleteSlices(BlockData::class.java)
|
|
||||||
mc.deleteSlices(String::class.java)
|
|
||||||
mc.deleteSlices(TileWrapper::class.java)
|
|
||||||
mc.flag(MantleFlag.PLANNED, false)
|
|
||||||
clearedCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!forceRegen && mc.isFlagged(MantleFlag.PLANNED)) {
|
|
||||||
plannedSkipped++
|
|
||||||
return@radius
|
|
||||||
}
|
|
||||||
|
|
||||||
for (c in pair.a) {
|
|
||||||
if (!forceRegen && mc.isFlagged(c.flag)) {
|
|
||||||
componentSkipped++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (forceRegen && mc.isFlagged(c.flag)) {
|
|
||||||
mc.flag(c.flag, false)
|
|
||||||
componentForcedReset++
|
|
||||||
}
|
|
||||||
|
|
||||||
launchedLayers++
|
|
||||||
|
|
||||||
launch(multicore) {
|
|
||||||
mc.raiseFlagSuspend(c.flag) {
|
|
||||||
c.generateLayer(writer, passX, passZ, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceRegen) {
|
|
||||||
Iris.info("Regen matter pass done: center=$x,$z passRadius=$passRadius rawPassRadius=$rawPassRadius visited=$visitedChunks cleared=$clearedCount dedupSkipped=$dedupSkipped plannedSkipped=$plannedSkipped componentSkipped=$componentSkipped componentForcedReset=$componentForcedReset launchedLayers=$launchedLayers flags=[$passFlags]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
radius(x, z, realRadius) { realX, realZ ->
|
|
||||||
val realKey = chunkKey(realX, realZ)
|
|
||||||
if (plannedChunks != null && !plannedChunks.add(realKey)) {
|
|
||||||
return@radius
|
|
||||||
}
|
|
||||||
writer.acquireChunk(realX, realZ)
|
|
||||||
.flag(MantleFlag.PLANNED, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceRegen) {
|
|
||||||
Iris.info("Regen matter done: center=$x,$z markedRealRadius=$realRadius forceRegen=$forceRegen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun radius(x: Int, z: Int, radius: Int, crossinline task: (Int, Int) -> Unit) {
|
|
||||||
for (i in -radius..radius) {
|
|
||||||
for (j in -radius..radius) {
|
|
||||||
task(x + i, z + j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val dispatcher = MultiBurst.burst.dispatcher//.limitedParallelism(128, "Mantle")
|
|
||||||
private const val regenPassCacheTtlMs = 600000L
|
|
||||||
private val regenGeneratedChunksByPass = ConcurrentHashMap<String, MutableSet<Long>>()
|
|
||||||
private val regenClearedChunksByPass = ConcurrentHashMap<String, MutableSet<Long>>()
|
|
||||||
private val regenPlannedChunksByPass = ConcurrentHashMap<String, MutableSet<Long>>()
|
|
||||||
private val regenPassTouchedMs = ConcurrentHashMap<String, Long>()
|
|
||||||
|
|
||||||
private fun CoroutineScope.launch(multicore: Boolean, block: suspend CoroutineScope.() -> Unit) =
|
|
||||||
launch(if (multicore) dispatcher else EmptyCoroutineContext, block = block)
|
|
||||||
|
|
||||||
private fun chunkKey(x: Int, z: Int): Long {
|
|
||||||
return (x.toLong() shl 32) xor (z.toLong() and 0xffffffffL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRegenPassSet(store: ConcurrentHashMap<String, MutableSet<Long>>, passKey: String): MutableSet<Long> {
|
|
||||||
return store.computeIfAbsent(passKey) { ConcurrentHashMap.newKeySet<Long>() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveRegenPassKey(threadName: String): String? {
|
|
||||||
val runtimeKey = RegenRuntime.getRunId()
|
|
||||||
if (!runtimeKey.isNullOrBlank()) {
|
|
||||||
return runtimeKey
|
|
||||||
}
|
|
||||||
if (!threadName.startsWith("Iris-Regen-")) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val suffix = threadName.substring("Iris-Regen-".length)
|
|
||||||
val lastDash = suffix.lastIndexOf('-')
|
|
||||||
if (lastDash <= 0) {
|
|
||||||
return suffix
|
|
||||||
}
|
|
||||||
return suffix.substring(0, lastDash)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun touchRegenPass(passKey: String) {
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
regenPassTouchedMs[passKey] = now
|
|
||||||
if (regenPassTouchedMs.size <= 64) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val iterator = regenPassTouchedMs.entries.iterator()
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
val entry = iterator.next()
|
|
||||||
if (now - entry.value <= regenPassCacheTtlMs) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val key = entry.key
|
|
||||||
iterator.remove()
|
|
||||||
regenGeneratedChunksByPass.remove(key)
|
|
||||||
regenClearedChunksByPass.remove(key)
|
|
||||||
regenPlannedChunksByPass.remove(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package art.arcane.iris.util.project.context
|
|
||||||
|
|
||||||
import art.arcane.iris.engine.IrisComplex
|
|
||||||
import art.arcane.iris.engine.`object`.IrisBiome
|
|
||||||
import art.arcane.iris.engine.`object`.IrisRegion
|
|
||||||
import art.arcane.iris.util.common.parallel.MultiBurst
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import org.bukkit.block.data.BlockData
|
|
||||||
|
|
||||||
class ChunkContext @JvmOverloads constructor(
|
|
||||||
val x: Int,
|
|
||||||
val z: Int,
|
|
||||||
c: IrisComplex,
|
|
||||||
cache: Boolean = true,
|
|
||||||
) {
|
|
||||||
val height: ChunkedDataCache<Double> = ChunkedDataCache(c.heightStream, x, z, cache)
|
|
||||||
val biome: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.trueBiomeStream, x, z, cache)
|
|
||||||
val cave: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.caveBiomeStream, x, z, cache)
|
|
||||||
val rock: ChunkedDataCache<BlockData> = ChunkedDataCache(c.rockStream, x, z, cache)
|
|
||||||
val fluid: ChunkedDataCache<BlockData> = ChunkedDataCache(c.fluidStream, x, z, cache)
|
|
||||||
val region: ChunkedDataCache<IrisRegion> = ChunkedDataCache(c.regionStream, x, z, cache)
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (cache) runBlocking {
|
|
||||||
val dispatcher = MultiBurst.burst.dispatcher
|
|
||||||
|
|
||||||
launch { height.fill(dispatcher) }
|
|
||||||
launch { biome.fill(dispatcher) }
|
|
||||||
launch { cave.fill(dispatcher) }
|
|
||||||
launch { rock.fill(dispatcher) }
|
|
||||||
launch { fluid.fill(dispatcher) }
|
|
||||||
launch { region.fill(dispatcher) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package art.arcane.iris.util.project.context
|
|
||||||
|
|
||||||
import art.arcane.volmlib.util.documentation.BlockCoordinates
|
|
||||||
import art.arcane.iris.util.project.stream.ProceduralStream
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
class ChunkedDataCache<T> private constructor(
|
|
||||||
private val x: Int,
|
|
||||||
private val z: Int,
|
|
||||||
private val stream: ProceduralStream<T?>,
|
|
||||||
private val cache: Boolean
|
|
||||||
) {
|
|
||||||
private val data = arrayOfNulls<Any>(if (cache) 256 else 0)
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
@BlockCoordinates
|
|
||||||
constructor(stream: ProceduralStream<T?>, x: Int, z: Int, cache: Boolean = true) : this(x, z, stream, cache)
|
|
||||||
|
|
||||||
suspend fun fill(context: CoroutineContext = Dispatchers.Default) {
|
|
||||||
if (!cache) return
|
|
||||||
|
|
||||||
supervisorScope {
|
|
||||||
for (j in 0 until 16) {
|
|
||||||
launch(context) {
|
|
||||||
val rowOffset = j * 16
|
|
||||||
val zz = (z + j).toDouble()
|
|
||||||
for (i in 0 until 16) {
|
|
||||||
data[rowOffset + i] = stream.get((x + i).toDouble(), zz)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BlockCoordinates
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun get(x: Int, z: Int): T? {
|
|
||||||
if (!cache) {
|
|
||||||
return stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
|
|
||||||
}
|
|
||||||
|
|
||||||
val t = data[(z * 16) + x] as? T
|
|
||||||
return t ?: stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,10 +39,9 @@ byte-buddy = "1.17.6" # https://central.sonatype.com/artifact/net.bytebuddy/byte
|
|||||||
dom4j = "2.2.0" # https://central.sonatype.com/artifact/org.dom4j/dom4j
|
dom4j = "2.2.0" # https://central.sonatype.com/artifact/org.dom4j/dom4j
|
||||||
jaxen = "2.0.0" # https://central.sonatype.com/artifact/jaxen/jaxen
|
jaxen = "2.0.0" # https://central.sonatype.com/artifact/jaxen/jaxen
|
||||||
|
|
||||||
# Script Engine
|
# Kotlin Runtime
|
||||||
kotlin = "2.2.0"
|
kotlin = "2.2.0"
|
||||||
kotlin-coroutines = "1.10.2"
|
kotlin-coroutines = "1.10.2"
|
||||||
maven-core = "3.9.10"
|
|
||||||
|
|
||||||
# Third Party Integrations
|
# Third Party Integrations
|
||||||
nexo = "1.10.0" # https://repo.nexomc.com/#/releases/com/nexomc/nexo
|
nexo = "1.10.0" # https://repo.nexomc.com/#/releases/com/nexomc/nexo
|
||||||
@@ -90,14 +89,9 @@ byteBuddy-agent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "by
|
|||||||
dom4j = { module = "org.dom4j:dom4j", version.ref = "dom4j" }
|
dom4j = { module = "org.dom4j:dom4j", version.ref = "dom4j" }
|
||||||
jaxen = { module = "jaxen:jaxen", version.ref = "jaxen" }
|
jaxen = { module = "jaxen:jaxen", version.ref = "jaxen" }
|
||||||
|
|
||||||
# Script Engine
|
# Kotlin Runtime
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
||||||
kotlin-scripting-common = { module = "org.jetbrains.kotlin:kotlin-scripting-common", version.ref = "kotlin" }
|
|
||||||
kotlin-scripting-jvm = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm", version.ref = "kotlin" }
|
|
||||||
kotlin-scripting-jvm-host = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm-host", version.ref = "kotlin" }
|
|
||||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
kotlin-scripting-dependencies-maven = { module = "org.jetbrains.kotlin:kotlin-scripting-dependencies-maven", version.ref = "kotlin" }
|
|
||||||
mavenCore = { module = "org.apache.maven:maven-core", version.ref = "maven-core" }
|
|
||||||
|
|
||||||
# Third Party Integrations
|
# Third Party Integrations
|
||||||
nexo = { module = "com.nexomc:nexo", version.ref = "nexo" }
|
nexo = { module = "com.nexomc:nexo", version.ref = "nexo" }
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = 'Iris'
|
||||||
|
|
||||||
|
boolean hasVolmLibSettings(File directory) {
|
||||||
|
new File(directory, 'settings.gradle.kts').exists() || new File(directory, 'settings.gradle').exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
File resolveLocalVolmLibDirectory() {
|
||||||
|
String configuredPath = providers.gradleProperty('localVolmLibDirectory')
|
||||||
|
.orElse(providers.environmentVariable('VOLMLIB_DIR'))
|
||||||
|
.orNull
|
||||||
|
if (configuredPath != null && !configuredPath.isBlank()) {
|
||||||
|
File configuredDirectory = file(configuredPath)
|
||||||
|
if (hasVolmLibSettings(configuredDirectory)) {
|
||||||
|
return configuredDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File currentDirectory = settingsDir
|
||||||
|
while (currentDirectory != null) {
|
||||||
|
File candidate = new File(currentDirectory, 'VolmLib')
|
||||||
|
if (hasVolmLibSettings(candidate)) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDirectory = currentDirectory.parentFile
|
||||||
|
}
|
||||||
|
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useLocalVolmLib = providers.gradleProperty('useLocalVolmLib')
|
||||||
|
.orElse('true')
|
||||||
|
.map { String value -> value.equalsIgnoreCase('true') }
|
||||||
|
.get()
|
||||||
|
File localVolmLibDirectory = resolveLocalVolmLibDirectory()
|
||||||
|
|
||||||
|
if (useLocalVolmLib && localVolmLibDirectory != null) {
|
||||||
|
includeBuild(localVolmLibDirectory) {
|
||||||
|
dependencySubstitution {
|
||||||
|
substitute(module('com.github.VolmitSoftware:VolmLib')).using(project(':shared'))
|
||||||
|
substitute(module('com.github.VolmitSoftware.VolmLib:shared')).using(project(':shared'))
|
||||||
|
substitute(module('com.github.VolmitSoftware.VolmLib:volmlib-shared')).using(project(':shared'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include(':core', ':core:agent')
|
||||||
|
include(':nms:v1_21_R7')
|
||||||
@@ -1,73 +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/>.
|
|
||||||
*/
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = "Iris"
|
|
||||||
|
|
||||||
fun hasVolmLibSettings(directory: File): Boolean {
|
|
||||||
return directory.resolve("settings.gradle.kts").exists() || directory.resolve("settings.gradle").exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resolveLocalVolmLibDirectory(): File? {
|
|
||||||
val configuredPath: String? = providers.gradleProperty("localVolmLibDirectory")
|
|
||||||
.orElse(providers.environmentVariable("VOLMLIB_DIR"))
|
|
||||||
.orNull
|
|
||||||
if (!configuredPath.isNullOrBlank()) {
|
|
||||||
val configuredDirectory: File = file(configuredPath)
|
|
||||||
if (hasVolmLibSettings(configuredDirectory)) {
|
|
||||||
return configuredDirectory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentDirectory: File? = settingsDir
|
|
||||||
while (currentDirectory != null) {
|
|
||||||
val candidate: File = currentDirectory.resolve("VolmLib")
|
|
||||||
if (hasVolmLibSettings(candidate)) {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDirectory = currentDirectory.parentFile
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib")
|
|
||||||
.orElse("true")
|
|
||||||
.map { value: String -> value.equals("true", ignoreCase = true) }
|
|
||||||
.get()
|
|
||||||
val localVolmLibDirectory: File? = resolveLocalVolmLibDirectory()
|
|
||||||
|
|
||||||
if (useLocalVolmLib && localVolmLibDirectory != null) {
|
|
||||||
includeBuild(localVolmLibDirectory) {
|
|
||||||
dependencySubstitution {
|
|
||||||
substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared"))
|
|
||||||
substitute(module("com.github.VolmitSoftware.VolmLib:shared")).using(project(":shared"))
|
|
||||||
substitute(module("com.github.VolmitSoftware.VolmLib:volmlib-shared")).using(project(":shared"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include(":core", ":core:agent")
|
|
||||||
include(
|
|
||||||
":nms:v1_21_R7",
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user