cleanup script engine

This commit is contained in:
Julian Krings 2025-07-31 00:16:23 +02:00
parent 8ddc8abdb9
commit 67f456cf53
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
16 changed files with 191 additions and 122 deletions

View File

@ -30,7 +30,6 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.scripting.ExecutionEnvironment;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.EnginePanic;
@ -447,7 +446,6 @@ public class Iris extends VolmitPlugin implements Listener {
IO.delete(new File("iris"));
compat = IrisCompat.configured(getDataFile("compat.json"));
ServerConfigurator.configure();
ExecutionEnvironment.createSimple();
IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag());
IrisSafeguard.splash(true);

View File

@ -24,7 +24,7 @@ import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.volmit.iris.Iris;
import com.volmit.iris.core.scripting.ExecutionEnvironment;
import com.volmit.iris.core.scripting.environment.PackEnvironment;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
@ -57,7 +57,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private static final KMap<File, IrisData> dataLoaders = new KMap<>();
private final File dataFolder;
private final int id;
private final ExecutionEnvironment.Pack environment;
private final PackEnvironment environment;
private boolean closed = false;
private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisLootTable> lootLoader;
@ -91,7 +91,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.engine = null;
this.dataFolder = dataFolder;
this.id = RNG.r.imax();
this.environment = ExecutionEnvironment.createPack(this);
this.environment = PackEnvironment.create(this);
hotloaded();
}

View File

@ -24,7 +24,7 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.scripting.ExecutionEnvironment;
import com.volmit.iris.core.scripting.environment.SimpleEnvironment;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.annotations.Snippet;
@ -355,7 +355,7 @@ public class IrisProject {
settings.put("json.schemas", schemas);
ws.put("settings", settings);
ExecutionEnvironment.createSimple().configureProject(path);
SimpleEnvironment.create().configureProject(path);
File schemasFile = new File(path, ".idea" + File.separator + "jsonSchemas.xml");
Document doc = IO.read(schemasFile);
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");

View File

@ -1,73 +0,0 @@
package com.volmit.iris.core.scripting;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
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 com.volmit.iris.util.math.RNG;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
@UtilityClass
public class ExecutionEnvironment {
@NonNull
public static Engine createEngine(@NonNull com.volmit.iris.engine.framework.Engine engine) {
return new IrisExecutionEnvironment(engine);
}
@NonNull
public static Pack createPack(@NonNull IrisData data) {
return new IrisPackExecutionEnvironment(data);
}
@NonNull
public static Simple createSimple() {
return new IrisSimpleExecutionEnvironment();
}
public interface Simple {
void configureProject(@NonNull File projectDir);
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);
default void close() {
}
}
public interface Pack extends Simple {
@NonNull
IrisData getData();
@Nullable
Object createNoise(@NonNull String script, @NonNull RNG rng);
}
public interface Engine extends Pack {
@NonNull
com.volmit.iris.engine.framework.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);
}
}

View File

@ -0,0 +1,25 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment;
import com.volmit.iris.engine.framework.Engine;
import lombok.NonNull;
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);
}

View File

@ -0,0 +1,19 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
import com.volmit.iris.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);
}

View File

@ -0,0 +1,30 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.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();
}
void configureProject(@NonNull File projectDir);
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);
default void close() {
}
}

View File

@ -28,7 +28,7 @@ import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.project.IrisProject;
import com.volmit.iris.core.scripting.ExecutionEnvironment;
import com.volmit.iris.core.scripting.environment.EngineEnvironment;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.*;
@ -94,7 +94,7 @@ public class IrisEngine implements Engine {
private CompletableFuture<Long> hash32;
private EngineMode mode;
private EngineEffects effects;
private ExecutionEnvironment.Engine execution;
private EngineEnvironment execution;
private EngineWorldManager worldManager;
private volatile int parallelism;
private boolean failing;
@ -170,7 +170,7 @@ public class IrisEngine implements Engine {
cacheId = RNG.r.nextInt();
worldManager = new IrisWorldManager(this);
complex = new IrisComplex(this);
execution = ExecutionEnvironment.createEngine(this);
execution = EngineEnvironment.create(this);
effects = new IrisEngineEffects(this);
hash32 = new CompletableFuture<>();
setupMode();

View File

@ -29,7 +29,7 @@ import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.scripting.ExecutionEnvironment;
import com.volmit.iris.core.scripting.environment.EngineEnvironment;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.data.cache.Cache;
@ -110,7 +110,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
IrisContext getContext();
ExecutionEnvironment.Engine getExecution();
EngineEnvironment getExecution();
double getMaxBiomeObjectDensity();

View File

@ -17,8 +17,8 @@ abstract class SimpleScript
object SimpleScriptDefinition : ScriptCompilationConfiguration({
isStandalone(false)
defaultImports(
"kotlin.script.experimental.dependencies.DependsOn",
"kotlin.script.experimental.dependencies.Repository",
DependsOn::class.qualifiedName!!,
Repository::class.qualifiedName!!,
"com.volmit.iris.Iris.info",
"com.volmit.iris.Iris.debug",
"com.volmit.iris.Iris.warn",

View File

@ -1,7 +1,7 @@
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.environment.EngineEnvironment
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
@ -13,7 +13,7 @@ import org.bukkit.entity.Entity
data class IrisExecutionEnvironment(
private val engine: Engine
) : IrisPackExecutionEnvironment(engine.data), ExecutionEnvironment.Engine {
) : IrisPackExecutionEnvironment(engine.data), EngineEnvironment {
override fun getEngine() = engine
override fun execute(script: String) =
@ -26,7 +26,7 @@ data class IrisExecutionEnvironment(
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,))
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`))

View File

@ -1,7 +1,7 @@
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.environment.PackEnvironment
import com.volmit.iris.core.scripting.kotlin.base.DataScript
import com.volmit.iris.core.scripting.kotlin.base.NoiseScript
import com.volmit.iris.core.scripting.kotlin.runner.Script
@ -11,14 +11,14 @@ import kotlin.reflect.KClass
open class IrisPackExecutionEnvironment(
private val data: IrisData
) : IrisSimpleExecutionEnvironment(), ExecutionEnvironment.Pack {
) : IrisSimpleExecutionEnvironment(data.dataFolder), PackEnvironment {
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) }
.computeIfAbsent(type) { _ -> runner.compile(type, loaded.loadFile, loaded.source) }
.valueOrThrow("Failed to compile script $script")
}

View File

@ -2,7 +2,7 @@ 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.environment.SimpleEnvironment
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
@ -18,9 +18,11 @@ import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.text.split
open class IrisSimpleExecutionEnvironment : ExecutionEnvironment.Simple {
open class IrisSimpleExecutionEnvironment(
baseDir: File = File(".").absoluteFile
) : SimpleEnvironment {
protected val compileCache = KCache<String, KMap<KClass<*>, ResultWithDiagnostics<Script>>>({ _ -> KMap() }, IrisSettings.get().performance.cacheSize.toLong())
protected val runner = ScriptRunner()
protected val runner = ScriptRunner(baseDir)
override fun execute(
script: String
@ -50,12 +52,12 @@ open class IrisSimpleExecutionEnvironment : ExecutionEnvironment.Simple {
override fun close() {
compileCache.invalidate()
runner.clearConfigurations()
runner.clear()
}
protected open fun compile(script: String, type: KClass<*>) =
compileCache.get(script)
.computeIfAbsent(type) { _ -> runner.compileText(type, 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? {

View File

@ -0,0 +1,65 @@
package com.volmit.iris.core.scripting.kotlin.runner
import java.io.File
import java.util.Collections.synchronizedList
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,
) : ExternalDependenciesResolver {
private val localRepos = synchronizedList(arrayListOf(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
}

View File

@ -1,11 +1,14 @@
package com.volmit.iris.core.scripting.kotlin.runner
import com.volmit.iris.core.scripting.kotlin.base.EngineScript
import com.volmit.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.*
import kotlin.script.experimental.dependencies.DependsOn
import kotlin.script.experimental.dependencies.Repository
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.host.withDefaultsFrom
@ -15,16 +18,22 @@ import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
class ScriptRunner(
private val host: BasicJvmScriptingHost
private val host: BasicJvmScriptingHost,
private val baseDir: File
) {
constructor() : this(BasicJvmScriptingHost())
constructor(baseDir: File) : this(BasicJvmScriptingHost(), baseDir)
private val configs = ConcurrentHashMap<KClass<*>, ScriptCompilationConfiguration>()
private val hostConfig = host.baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
private var resolver = createResolver(baseDir)
fun compileText(type: KClass<*>, raw: String, name: String? = null) = compile(type, raw.toScriptSource(name))
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))
fun clearConfigurations() = configs.clear()
fun clear() {
configs.clear()
resolver = createResolver(baseDir)
}
private fun compile(
type: KClass<*>,
@ -39,11 +48,15 @@ class ScriptRunner(
hostConfig,
type
) {
if (EngineScript::class.java.isAssignableFrom(type.java))
dependencyResolver(resolver)
if (SimpleScript::class.java.isAssignableFrom(type.java))
return@createCompilationConfigurationFromTemplate
jvm {
dependenciesFromClassContext(type, wholeClasspath = true)
dependenciesFromClassContext(this::class, wholeClasspath = true)
dependenciesFromClassContext(KotlinScript::class, wholeClasspath = true)
}
refineConfiguration {

View File

@ -2,7 +2,6 @@ package com.volmit.iris.core.scripting.kotlin.runner
import kotlinx.coroutines.runBlocking
import java.io.File
import java.lang.RuntimeException
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.CompoundDependenciesResolver
import kotlin.script.experimental.dependencies.FileSystemDependenciesResolver
@ -10,6 +9,7 @@ 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
import kotlin.script.experimental.util.PropertiesCollection
internal fun <T, R> ResultWithDiagnostics<T>.map(transformer: (T) -> R): ResultWithDiagnostics<R> = when (this) {
is ResultWithDiagnostics.Success -> ResultWithDiagnostics.Success(transformer(value), reports)
@ -23,11 +23,14 @@ internal fun ResultValue.valueOrNull(): Any? =
else -> null
}
internal fun createResolver(baseDir: File = File(".").normalize()) = CompoundDependenciesResolver(FileDependenciesResolver(baseDir), MavenDependenciesResolver())
internal val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
private val resolver = createResolver()
internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
?: return context.compilationConfiguration.asSuccess()
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
return runBlocking {
resolver.resolveFromScriptSourceAnnotations(annotations)
}.onSuccess {
@ -37,23 +40,10 @@ internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinem
}
}
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
}
}
fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R = valueOr {
throw RuntimeException(it.reports.joinToString("\n", "$message\n") { r -> r.render(withStackTrace = true) })
}
}
val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver)