mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-21 11:43:27 +00:00
merge kts engine into this codebase
This commit is contained in:
parent
4b0766c097
commit
1bc6192c8b
@ -26,6 +26,8 @@ plugins {
|
|||||||
alias(libs.plugins.shadow)
|
alias(libs.plugins.shadow)
|
||||||
alias(libs.plugins.sentry)
|
alias(libs.plugins.sentry)
|
||||||
alias(libs.plugins.slimjar)
|
alias(libs.plugins.slimjar)
|
||||||
|
alias(libs.plugins.kotlin.jvm)
|
||||||
|
alias(libs.plugins.kotlin.lombok)
|
||||||
}
|
}
|
||||||
|
|
||||||
val apiVersion = "1.19"
|
val apiVersion = "1.19"
|
||||||
@ -91,6 +93,14 @@ dependencies {
|
|||||||
slim(libs.byteBuddy.agent)
|
slim(libs.byteBuddy.agent)
|
||||||
slim(libs.dom4j)
|
slim(libs.dom4j)
|
||||||
slim(libs.jaxen)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -117,6 +127,9 @@ slimJar {
|
|||||||
relocate("net.kyori", "$lib.kyori")
|
relocate("net.kyori", "$lib.kyori")
|
||||||
relocate("org.bstats", "$lib.metrics")
|
relocate("org.bstats", "$lib.metrics")
|
||||||
relocate("io.sentry", "$lib.sentry")
|
relocate("io.sentry", "$lib.sentry")
|
||||||
|
relocate("org.apache.maven", "$lib.maven")
|
||||||
|
relocate("org.codehaus.plexus", "$lib.plexus")
|
||||||
|
relocate("org.eclipse.sisu", "$lib.sisu")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
@ -53,6 +53,7 @@ import com.volmit.iris.util.io.JarScanner;
|
|||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
import com.volmit.iris.util.math.RNG;
|
import com.volmit.iris.util.math.RNG;
|
||||||
import com.volmit.iris.util.misc.Bindings;
|
import com.volmit.iris.util.misc.Bindings;
|
||||||
|
import com.volmit.iris.util.misc.SlimJar;
|
||||||
import com.volmit.iris.util.misc.getHardware;
|
import com.volmit.iris.util.misc.getHardware;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
import com.volmit.iris.util.plugin.IrisService;
|
import com.volmit.iris.util.plugin.IrisService;
|
||||||
@ -438,13 +439,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Iris() {
|
public Iris() {
|
||||||
ApplicationBuilder.appending("Iris")
|
SlimJar.load(getDataFolder("cache", "libraries"));
|
||||||
.downloadDirectoryPath(getDataFolder("cache", "libraries").toPath())
|
|
||||||
.logger((message, args) -> {
|
|
||||||
if (!message.startsWith("Loaded library ")) return;
|
|
||||||
getLogger().info(message.formatted(args));
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enable() {
|
private void enable() {
|
||||||
|
@ -1,81 +1,37 @@
|
|||||||
package com.volmit.iris.core.scripting;
|
package com.volmit.iris.core.scripting;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.loader.IrisRegistrant;
|
import com.volmit.iris.core.loader.IrisRegistrant;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment;
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class ExecutionEnvironment {
|
public class ExecutionEnvironment {
|
||||||
private static final String VERSION = System.getProperty("iris.scriptVersion", "master-ffbf167eba-1");
|
|
||||||
private static final String BASE_URL = "https://jitpack.io/com/github/VolmitSoftware/Iris-Scripts/" + VERSION + "/Iris-Scripts-" + VERSION + "-all.jar";
|
|
||||||
private static final Provider PROVIDER = ServiceLoader.load(Provider.class, buildLoader())
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow()
|
|
||||||
.init(Iris.instance.getDataFolder("cache", "libraries").toPath());
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Engine createEngine(@NonNull com.volmit.iris.engine.framework.Engine engine) {
|
public static Engine createEngine(@NonNull com.volmit.iris.engine.framework.Engine engine) {
|
||||||
return PROVIDER.createEngine(engine);
|
return new IrisExecutionEnvironment(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Pack createPack(@NonNull IrisData data) {
|
public static Pack createPack(@NonNull IrisData data) {
|
||||||
return PROVIDER.createPack(data);
|
return new IrisPackExecutionEnvironment(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Simple createSimple() {
|
public static Simple createSimple() {
|
||||||
return PROVIDER.createSimple();
|
return new IrisSimpleExecutionEnvironment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private static URLClassLoader buildLoader() {
|
|
||||||
try (HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build()) {
|
|
||||||
var resolved = client.send(HttpRequest.newBuilder(URI.create(BASE_URL)).build(), HttpResponse.BodyHandlers.discarding())
|
|
||||||
.request();
|
|
||||||
String hash = IO.hash(resolved.uri().getPath());
|
|
||||||
File file = Iris.instance.getDataFile("cache", hash.substring(0, 2), hash.substring(3, 5), hash + ".jar");
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.getParentFile().mkdirs();
|
|
||||||
Iris.info("Downloading Script Engine...");
|
|
||||||
client.send(resolved, HttpResponse.BodyHandlers.ofFile(file.toPath()));
|
|
||||||
Iris.info("Downloaded Script Engine!");
|
|
||||||
}
|
|
||||||
return new URLClassLoader(new URL[]{file.toURI().toURL()}, Provider.class.getClassLoader());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Provider {
|
|
||||||
Provider init(Path localRepository);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Engine createEngine(@NonNull com.volmit.iris.engine.framework.Engine engine);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Pack createPack(@NonNull IrisData data);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Simple createSimple();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface Simple {
|
public interface Simple {
|
||||||
void configureProject(@NonNull File projectDir);
|
void configureProject(@NonNull File projectDir);
|
||||||
|
|
||||||
|
37
core/src/main/java/com/volmit/iris/util/misc/SlimJar.java
Normal file
37
core/src/main/java/com/volmit/iris/util/misc/SlimJar.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.volmit.iris.util.misc;
|
||||||
|
|
||||||
|
import io.github.slimjar.app.builder.ApplicationBuilder;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class SlimJar {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger("Iris");
|
||||||
|
private static final ReentrantLock lock = new ReentrantLock();
|
||||||
|
private static final AtomicBoolean loaded = new AtomicBoolean();
|
||||||
|
|
||||||
|
public static void load(@Nullable File localRepository) {
|
||||||
|
if (loaded.get()) return;
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (loaded.getAndSet(true)) return;
|
||||||
|
if (localRepository == null) {
|
||||||
|
localRepository = new File(".iris/libraries");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationBuilder.appending("Iris")
|
||||||
|
.downloadDirectoryPath(localRepository.toPath())
|
||||||
|
.logger((message, args) -> {
|
||||||
|
if (!message.startsWith("Loaded library ")) return;
|
||||||
|
LOGGER.info(message.formatted(args));
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.base
|
||||||
|
|
||||||
|
import com.volmit.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
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.base
|
||||||
|
|
||||||
|
import com.volmit.iris.core.scripting.func.BiomeLookup
|
||||||
|
import com.volmit.iris.engine.IrisComplex
|
||||||
|
import com.volmit.iris.engine.framework.Engine
|
||||||
|
import com.volmit.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,
|
||||||
|
"complex" to IrisComplex::class,
|
||||||
|
"seed" to Long::class,
|
||||||
|
"dimension" to IrisDimension::class,
|
||||||
|
"biome" to BiomeLookup::class,
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
|
||||||
|
private fun readResolve(): Any = EngineScriptDefinition
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.volmit.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
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.volmit.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
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.base
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisRegistrant
|
||||||
|
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(EngineScriptDefinition), {
|
||||||
|
providedProperties("object" to IrisRegistrant::class)
|
||||||
|
}) {
|
||||||
|
private fun readResolve(): Any = PreprocessorScriptDefinition
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.base
|
||||||
|
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.configureMavenDepsOnAnnotations
|
||||||
|
import com.volmit.iris.util.misc.SlimJar
|
||||||
|
import kotlin.script.experimental.annotations.KotlinScript
|
||||||
|
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||||
|
import kotlin.script.experimental.api.defaultImports
|
||||||
|
import kotlin.script.experimental.api.isStandalone
|
||||||
|
import kotlin.script.experimental.api.refineConfiguration
|
||||||
|
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({
|
||||||
|
SlimJar.load(null)
|
||||||
|
|
||||||
|
isStandalone(false)
|
||||||
|
defaultImports(
|
||||||
|
"kotlin.script.experimental.dependencies.DependsOn",
|
||||||
|
"kotlin.script.experimental.dependencies.Repository",
|
||||||
|
"com.volmit.iris.Iris.info",
|
||||||
|
"com.volmit.iris.Iris.debug",
|
||||||
|
"com.volmit.iris.Iris.warn",
|
||||||
|
"com.volmit.iris.Iris.error"
|
||||||
|
)
|
||||||
|
|
||||||
|
jvm {
|
||||||
|
dependenciesFromClassContext(SimpleScript::class, wholeClasspath = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
refineConfiguration {
|
||||||
|
onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
private fun readResolve(): Any = SimpleScriptDefinition
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.environment
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisRegistrant
|
||||||
|
import com.volmit.iris.core.scripting.ExecutionEnvironment
|
||||||
|
import com.volmit.iris.core.scripting.func.BiomeLookup
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.EngineScript
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.MobSpawningScript
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.PostMobSpawningScript
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.PreprocessorScript
|
||||||
|
import com.volmit.iris.engine.framework.Engine
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.entity.Entity
|
||||||
|
|
||||||
|
data class IrisExecutionEnvironment(
|
||||||
|
private val engine: Engine
|
||||||
|
) : IrisPackExecutionEnvironment(engine.data), ExecutionEnvironment.Engine {
|
||||||
|
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.parameters("object" to `object`))
|
||||||
|
|
||||||
|
private fun Engine.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
|
||||||
|
return mapOf(
|
||||||
|
"data" to data,
|
||||||
|
"engine" to this,
|
||||||
|
"complex" to complex,
|
||||||
|
"seed" to seedManager.seed,
|
||||||
|
"dimension" to dimension,
|
||||||
|
"biome" to BiomeLookup(::getSurfaceBiome),
|
||||||
|
*values,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.environment
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData
|
||||||
|
import com.volmit.iris.core.scripting.ExecutionEnvironment
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.DataScript
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.Script
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.valueOrThrow
|
||||||
|
|
||||||
|
open class IrisPackExecutionEnvironment(
|
||||||
|
private val data: IrisData
|
||||||
|
) : IrisSimpleExecutionEnvironment(), ExecutionEnvironment.Pack {
|
||||||
|
|
||||||
|
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.compileText(type, loaded.source, script) }
|
||||||
|
.valueOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(script: String) =
|
||||||
|
execute(script, DataScript::class.java, data.parameters())
|
||||||
|
|
||||||
|
override fun evaluate(script: String) =
|
||||||
|
evaluate(script, DataScript::class.java, data.parameters())
|
||||||
|
|
||||||
|
private fun IrisData.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
|
||||||
|
return mapOf(
|
||||||
|
"data" to this,
|
||||||
|
*values,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.environment
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris
|
||||||
|
import com.volmit.iris.core.IrisSettings
|
||||||
|
import com.volmit.iris.core.scripting.ExecutionEnvironment
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.*
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.Script
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.classpath
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.valueOrNull
|
||||||
|
import com.volmit.iris.util.collection.KMap
|
||||||
|
import com.volmit.iris.util.data.KCache
|
||||||
|
import com.volmit.iris.util.format.C
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.valueOrThrow
|
||||||
|
import kotlin.text.split
|
||||||
|
|
||||||
|
open class IrisSimpleExecutionEnvironment : ExecutionEnvironment.Simple {
|
||||||
|
protected val compileCache = KCache<String, KMap<KClass<*>, ResultWithDiagnostics<Script>>>({ _ -> KMap() }, IrisSettings.get().performance.cacheSize.toLong())
|
||||||
|
protected val runner = ScriptRunner()
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
compileCache.invalidate()
|
||||||
|
runner.clearConfigurations()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun compile(script: String, type: KClass<*>) =
|
||||||
|
compileCache.get(script)
|
||||||
|
.computeIfAbsent(type) { _ -> runner.compileText(type, script) }
|
||||||
|
.valueOrThrow()
|
||||||
|
|
||||||
|
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()
|
||||||
|
.valueOrNull()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
current.contextClassLoader = loader
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configureProject(projectDir: File) {
|
||||||
|
projectDir.mkdirs()
|
||||||
|
val libs = javaClass.classLoader.parent.classpath
|
||||||
|
.sortedBy { it.absolutePath }
|
||||||
|
.toMutableList()
|
||||||
|
libs.add(codeSource)
|
||||||
|
libs.removeIf { libs.count { f -> f.name == it.name } > 1 }
|
||||||
|
|
||||||
|
File(projectDir, "build.gradle.kts")
|
||||||
|
.updateClasspath(libs)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CLASSPATH = "val classpath = files("
|
||||||
|
private val codeSource = File(IrisSimpleExecutionEnvironment::class.java.protectionDomain.codeSource.location.toURI())
|
||||||
|
|
||||||
|
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 classpath = classpath.joinToString(",", CLASSPATH, ")") { "\"${it.escapedPath}\"" }
|
||||||
|
val index = indexOfFirst { it.startsWith(CLASSPATH) }
|
||||||
|
if (index == -1) {
|
||||||
|
return "$classpath\n${joinToString("\n")}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val mod = toMutableList()
|
||||||
|
mod[index] = classpath
|
||||||
|
return mod.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val File.escapedPath
|
||||||
|
get() = absolutePath.replace("\\", "\\\\").replace("\"", "\\\"")
|
||||||
|
|
||||||
|
private val BASE_GRADLE = """
|
||||||
|
val classpath = files()
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version("2.1.20")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
add("script", classpath)
|
||||||
|
}""".trimIndent().split("\n")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
||||||
|
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()
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
val cached = base.getClass(scriptEvaluationConfiguration)
|
||||||
|
updater.set(this, cached)
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun evaluate(properties: Map<String, Any?>?) = host.runInCoroutineContext {
|
||||||
|
host.evaluator.invoke(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val updater = AtomicReferenceFieldUpdater.newUpdater(CachedScript::class.java, ResultWithDiagnostics::class.java, "value")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.volmit.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>
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner
|
||||||
|
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.base.EngineScript
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.dependencies.DependsOn
|
||||||
|
import kotlin.script.experimental.dependencies.Repository
|
||||||
|
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(
|
||||||
|
private val host: BasicJvmScriptingHost
|
||||||
|
) {
|
||||||
|
constructor() : this(BasicJvmScriptingHost())
|
||||||
|
|
||||||
|
private val configs = ConcurrentHashMap<KClass<*>, ScriptCompilationConfiguration>()
|
||||||
|
private val hostConfig = host.baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
|
||||||
|
|
||||||
|
fun compileText(type: KClass<*>, raw: String, name: String? = null) = compile(type, raw.toScriptSource(name))
|
||||||
|
|
||||||
|
fun clearConfigurations() = configs.clear()
|
||||||
|
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
if (EngineScript::class.java.isAssignableFrom(type.java))
|
||||||
|
return@createCompilationConfigurationFromTemplate
|
||||||
|
|
||||||
|
jvm {
|
||||||
|
dependenciesFromClassContext(type, wholeClasspath = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
refineConfiguration {
|
||||||
|
onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.dependencies.CompoundDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.FileSystemDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.resolveFromScriptSourceAnnotations
|
||||||
|
import kotlin.script.experimental.jvm.updateClasspath
|
||||||
|
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
||||||
|
|
||||||
|
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.valueOrNull() = returnValue.valueOrNull()
|
||||||
|
internal fun ResultValue.valueOrNull(): Any? =
|
||||||
|
when (this) {
|
||||||
|
is ResultValue.Value -> value
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
|
||||||
|
internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||||
|
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
||||||
|
?: return context.compilationConfiguration.asSuccess()
|
||||||
|
return runBlocking {
|
||||||
|
resolver.resolveFromScriptSourceAnnotations(annotations)
|
||||||
|
}.onSuccess {
|
||||||
|
context.compilationConfiguration.with {
|
||||||
|
updateClasspath(it)
|
||||||
|
}.asSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Collection<File>.format(projectDir: File): Collection<String> {
|
||||||
|
val projectDir = projectDir.absolutePath
|
||||||
|
val home = File(System.getProperty("user.home")).absolutePath
|
||||||
|
return map { format(it, projectDir, home) }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList()
|
||||||
|
|
||||||
|
private fun format(file: File, projectDir: String, home: String): String {
|
||||||
|
val path = file.absolutePath
|
||||||
|
return when {
|
||||||
|
path.startsWith(projectDir) -> $$"$PROJECT_DIR$/$${path.substring(projectDir.length + 1)}"
|
||||||
|
path.startsWith(home) -> $$"$USER_HOME$/$${path.substring(home.length + 1)}"
|
||||||
|
else -> path
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,10 @@ 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 = "2.2.0"
|
||||||
|
kotlin-coroutines = "1.10.2"
|
||||||
|
|
||||||
# Third Party Integrations
|
# Third Party Integrations
|
||||||
nexo = "1.8.0" # https://repo.nexomc.com/#/releases/com/nexomc/nexo
|
nexo = "1.8.0" # https://repo.nexomc.com/#/releases/com/nexomc/nexo
|
||||||
itemsadder = "4.0.10" # https://github.com/LoneDev6/API-ItemsAdder
|
itemsadder = "4.0.10" # https://github.com/LoneDev6/API-ItemsAdder
|
||||||
@ -82,6 +86,14 @@ 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-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-scripting-dependencies-maven = { module = "org.jetbrains.kotlin:kotlin-scripting-dependencies-maven", version.ref = "kotlin"}
|
||||||
|
|
||||||
# Third Party Integrations
|
# Third Party Integrations
|
||||||
nexo = { module = "com.nexomc:nexo", version.ref = "nexo" }
|
nexo = { module = "com.nexomc:nexo", version.ref = "nexo" }
|
||||||
itemsadder = { module = "dev.lone:api-itemsadder", version.ref = "itemsadder" }
|
itemsadder = { module = "dev.lone:api-itemsadder", version.ref = "itemsadder" }
|
||||||
@ -100,3 +112,5 @@ slimjar = { id = "de.crazydev22.slimjar", version.ref = "slimjar" }
|
|||||||
download = { id = "de.undercouch.download", version.ref = "download" }
|
download = { id = "de.undercouch.download", version.ref = "download" }
|
||||||
runPaper = { id = "xyz.jpenilla.run-paper", version.ref = "runPaper" }
|
runPaper = { id = "xyz.jpenilla.run-paper", version.ref = "runPaper" }
|
||||||
sentry = { id = "io.sentry.jvm.gradle", version.ref = "sentryPlugin" }
|
sentry = { id = "io.sentry.jvm.gradle", version.ref = "sentryPlugin" }
|
||||||
|
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
kotlin-lombok = { id = "org.jetbrains.kotlin.plugin.lombok", version.ref = "kotlin" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user