mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
First draft of KotlinPluginEnvironment
This commit is contained in:
@@ -1,7 +1,32 @@
|
||||
description = 'Apollo Game'
|
||||
|
||||
buildscript {
|
||||
ext.kotlinVersion = '1.1.2-4'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile project(':cache')
|
||||
compile project(':net')
|
||||
compile project(':util')
|
||||
|
||||
compile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler', version: $kotlinVersion
|
||||
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jre8', version: $kotlinVersion
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.kotlin.srcDirs += 'src/kotlin'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.apollo.game.plugin.kotlin
|
||||
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.apollo.Server
|
||||
import org.apollo.game.model.World
|
||||
import org.apollo.game.plugin.PluginContext
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.addKotlinSourceRoot
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import java.io.File
|
||||
|
||||
class KotlinPluginCompiler(val classpath: List<File>, val messageCollector: MessageCollector) {
|
||||
|
||||
private fun createCompilerConfiguration(inputPath: String): CompilerConfiguration {
|
||||
val configuration = CompilerConfiguration()
|
||||
val scriptDefinition = KotlinScriptDefinitionFromAnnotatedTemplate(KotlinPluginScript::class)
|
||||
|
||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||
configuration.put(JVMConfigurationKeys.CONTENT_ROOTS, classpath.map { JvmClasspathRoot(it) })
|
||||
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, inputPath)
|
||||
configuration.addKotlinSourceRoot(inputPath)
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
@Throws(KotlinPluginCompilerException::class)
|
||||
fun compile(inputPath: String): Class<out KotlinPluginScript> {
|
||||
val rootDisposable = Disposer.newDisposable()
|
||||
val configuration = createCompilerConfiguration(inputPath)
|
||||
|
||||
val configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, configFiles)
|
||||
|
||||
try {
|
||||
val clazz = KotlinToJVMBytecodeCompiler.compileScript(environment, Server::class.java.classLoader)
|
||||
|
||||
if (clazz?.getConstructor(World::class.java, PluginContext::class.java) == null) {
|
||||
throw KotlinPluginCompilerException("Unable to compile $inputPath, no plugin constructor found")
|
||||
}
|
||||
|
||||
return clazz as Class<out KotlinPluginScript>
|
||||
} catch (e: CompilationException) {
|
||||
throw KotlinPluginCompilerException("Compilation failed", e)
|
||||
} finally {
|
||||
Disposer.dispose(rootDisposable)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class KotlinPluginCompilerException(message: String, cause: Throwable? = null) : Exception(message, cause) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.apollo.game.plugin.kotlin
|
||||
|
||||
import org.apollo.game.message.handler.MessageHandler
|
||||
import org.apollo.game.model.World
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.plugin.PluginContext
|
||||
import org.apollo.net.message.Message
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.templates.ScriptTemplateDefinition
|
||||
|
||||
@ScriptTemplateDefinition(
|
||||
scriptFilePattern = ".*\\.plugin\\.kts"
|
||||
)
|
||||
abstract class KotlinPluginScript(val world: World, val context: PluginContext) {
|
||||
|
||||
protected fun <T : Message> on(type: () -> KClass<T>): KotlinMessageHandler<T> {
|
||||
return KotlinMessageHandler(world, context, type.invoke())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class KotlinMessageHandler<T : Message>(val world: World, val context: PluginContext, val type: KClass<T>) : MessageHandler<T>(world) {
|
||||
|
||||
override fun handle(player: Player, message: T) {
|
||||
if (message.predicate()) {
|
||||
message.function(player)
|
||||
}
|
||||
}
|
||||
|
||||
var function: T.(Player) -> Unit = { _ -> }
|
||||
|
||||
var predicate: T.() -> Boolean = { true }
|
||||
|
||||
fun where(predicate: T.() -> Boolean): KotlinMessageHandler<T> {
|
||||
this.predicate = predicate
|
||||
return this
|
||||
}
|
||||
|
||||
fun then(function: T.(Player) -> Unit) {
|
||||
this.function = function
|
||||
this.context.addMessageHandler(type.java, this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.apollo.game.plugin;
|
||||
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.game.plugin.kotlin.KotlinPluginCompiler;
|
||||
import org.apollo.game.plugin.kotlin.KotlinPluginScript;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation;
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity;
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class KotlinPluginEnvironment implements PluginEnvironment, MessageCollector {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(KotlinPluginEnvironment.class.getName());
|
||||
|
||||
private final World world;
|
||||
private final KotlinPluginCompiler pluginCompiler;
|
||||
|
||||
private PluginContext context;
|
||||
|
||||
public KotlinPluginEnvironment(World world) {
|
||||
this.world = world;
|
||||
this.pluginCompiler = new KotlinPluginCompiler(resolveClasspath(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the classpath of the current running {@link Thread}.
|
||||
*
|
||||
* @return A {@link List} of {@link File}s pointing to classpath entries.
|
||||
*/
|
||||
private static List<File> resolveClasspath() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (!(classLoader instanceof URLClassLoader)) {
|
||||
throw new RuntimeException("Unable to resolve classpath for current ClassLoader");
|
||||
}
|
||||
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
|
||||
URL[] classpathUrls = urlClassLoader.getURLs();
|
||||
List<File> classpath = new ArrayList<>();
|
||||
|
||||
for (URL classpathUrl : classpathUrls) {
|
||||
try {
|
||||
classpath.add(new File(classpathUrl.toURI()));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("URL returned by ClassLoader is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return classpath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream is, String name) {
|
||||
//@todo - wait until all plugin classes are loading until running constructors?
|
||||
try {
|
||||
Class<? extends KotlinPluginScript> pluginClass = pluginCompiler.compile(name);
|
||||
Constructor<? extends KotlinPluginScript> pluginConstructor = pluginClass.getConstructor(World.class,
|
||||
PluginContext.class);
|
||||
|
||||
pluginConstructor.newInstance(world, context);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(PluginContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(@NotNull CompilerMessageSeverity severity, @NotNull String message,
|
||||
@NotNull CompilerMessageLocation location) {
|
||||
if (severity.isError()) {
|
||||
logger.log(Level.SEVERE, String.format("%s:%s-%s: %s", location.getPath(), location.getLine(),
|
||||
location.getColumn(), message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasErrors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public final class PluginManager {
|
||||
Map<String, PluginMetaData> plugins = createMap(findPlugins());
|
||||
Set<PluginMetaData> started = new HashSet<>();
|
||||
|
||||
PluginEnvironment env = new RubyPluginEnvironment(world); // TODO isolate plugins if possible in the future!
|
||||
PluginEnvironment env = new KotlinPluginEnvironment(world); // TODO isolate plugins if possible in the future!
|
||||
env.setContext(context);
|
||||
|
||||
for (PluginMetaData plugin : plugins.values()) {
|
||||
|
||||
Reference in New Issue
Block a user