diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml
index d7bfcf7f..917d309e 100644
--- a/.github/workflows/build-dev.yml
+++ b/.github/workflows/build-dev.yml
@@ -34,6 +34,17 @@ jobs:
zip -r ../ScriptableMC-JavaScript-Lib.zip ./*
cd ../
cp ./ScriptableMC-JavaScript-Lib.zip ../../ScriptableMC-Engine-JS/src/main/resources/libraries.zip
+ - name: Export lib-smc with gradle
+ run: ./gradlew :ScriptableMC-Tools-TS:generateLibSMC
+ - name: Compile lib-smc, create archive, and publish
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ cd ./ScriptableMC-Tools-TS/lib-smc/
+ npm install
+ npm run compile
+ npm publish ./js
+ zip -r ./Lib-SMC.zip ./*
- name: Build All Plugins with Gradle
run: ./gradlew :shadowJarAll
- name: Copy artifacts
@@ -45,6 +56,7 @@ jobs:
cp ./build/ScriptableMC-Engine-PY-Bundled.jar ./artifacts/
cp ./ScriptableMC-Tools-TS/lib/ScriptableMC-TypeScript-Lib.zip ./artifacts/
cp ./ScriptableMC-Tools-TS/lib/ScriptableMC-JavaScript-Lib.zip ./artifacts/
+ cp ./ScriptableMC-Tools-TS/lib-smc/Lib-SMC.zip ./artifacts/
- uses: actions/upload-artifact@v1
with:
name: ScriptableMC-Engine-DEV
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c6dbf2a2..20a98845 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -30,6 +30,18 @@ jobs:
zip -r ../ScriptableMC-JavaScript-Lib.zip ./*
cd ../
cp ./ScriptableMC-JavaScript-Lib.zip ../../ScriptableMC-Engine-JS/src/main/resources/libraries.zip
+ - name: Export lib-smc with gradle
+ run: ./gradlew :ScriptableMC-Tools-TS:generateLibSMCRelease
+ - name: Compile lib-smc, create archive, and publish
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: |
+ cd ./ScriptableMC-Tools-TS/lib-smc/
+ npm install
+ npm run compile
+ npm publish ./js --access public
+ zip -r ./Lib-SMC.zip ./*
- name: Build JS Engine with Gradle
run: ./gradlew :shadowJarAll
- name: Upload JavaScript Engine Plugin Jar
@@ -49,13 +61,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- args: 'ScriptableMC-Engine-JS-Bundled/build/libs/ScriptableMC-Engine-JS-Bundled.jar'
+ args: 'ScriptableMC-Engine-JS/Bundled/build/libs/ScriptableMC-Engine-JS-Bundled.jar'
- name: Upload Bundled Python Engine Plugin Jar
uses: skx/github-action-publish-binaries@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- args: 'ScriptableMC-Engine-PY-Bundled/build/libs/ScriptableMC-Engine-PY-Bundled.jar'
+ args: 'ScriptableMC-Engine-PY/Bundled/build/libs/ScriptableMC-Engine-PY-Bundled.jar'
- name: Upload TypeScript Libraries
uses: skx/github-action-publish-binaries@master
env:
diff --git a/.gitignore b/.gitignore
index 929cb5f1..e19ebaef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,4 +42,5 @@ node_modules/
.rpt2_cache/
libraries/tools/kotlin-test-nodejs-runner/lib/
local.properties
-lib
\ No newline at end of file
+lib
+lib-smc
\ No newline at end of file
diff --git a/README.md b/README.md
index edd5de50..69f12399 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
[](https://github.com/astorks/ScriptableMC-Engine/releases/latest)
[](https://www.spigotmc.org/resources/scriptablemc-engine.74690/)
+
Run JavaScript/TypeScript plugins for Minecraft 1.15 using the GraalJS script engine.
Supported Minecraft Versions: Bukkit/Spigot/Paper 1.12, 1.13, 1.14, **1.15**
@@ -15,25 +16,28 @@ Most linux servers already use OpenJDK, however if you're running windows or wan
GraalVM-CE is a free open source java runtime created by Oracle that is pre-packaged with the GraalJS Engine allowing all javascript engine features including AOT compilation of javascript.
-## Installing The Plugin
-- Ensure your server is running OpenJDK 8+ or GraalVM
-- [Download the latest ScriptableMC plugin](https://github.com/astorks/ScriptableMC-Engine/releases/latest) and place it in your plugins folder.
-- Take a look at [ScriptableMC-TypeScript](https://github.com/astorks/ScriptableMC-TypeScript) for a full typescript plugin example.
+## Installing The JavaScript Engine Plugin
+##### If your server is running on a Standard JDK
+- Download the latest [`ScriptableMC-Engine-JS-Bundled.jar`](https://github.com/astorks/ScriptableMC-Engine/releases/latest/download/ScriptableMC-Engine-JS-Bundled.jar) and place it in your plugins folder.
+##### If your server is running on a GraalVM JDK
+- Download the latest [`ScriptableMC-Engine-JS.jar`](https://github.com/astorks/ScriptableMC-Engine/releases/latest/download/ScriptableMC-Engine-JS.jar) and place it in your plugins folder.
+
+Take a look at [ScriptableMC-TypeScript](https://github.com/astorks/ScriptableMC-TypeScript) for a full typescript plugin example.
You can take the typescript example and compile it, then directly modify the javascript if you don't want to use typescript.
## Commands and Permissions
##### ScritableMC Base Command
| Command | Alias | Description | Permission |
| ------------- | ------------- |-----------------------------------------------------| ------------------------ |
-| `/scriptablemc menu` | `/smc m` | Opens an inventory menu that allows you to control the scriptable engine. | `scriptablemc.menu` |
-| `/scriptablemc info` | `/smc i` | Prints plugin version and GraalVM/GraalJS versions if available. | `scriptablemc.info` |
-| `/scriptablemc reload` | `/smc rl` | Fully reloads all script engines. | `scriptablemc.reload` |
+| `/scriptablemc menu` | `/smc m` | Opens an inventory menu that allows you to control the script engines. | `scriptablemc.menu` |
+| `/scriptablemc info` | `/smc i` | Prints plugin version and all loaded script engine versions if available. | `scriptablemc.info` |
+| `/scriptablemc reload` | `/smc rl` | Fully reloads all script engines and all script files. | `scriptablemc.reload` |
| `/scriptablemc version` | `/smc v` | Check the github releases for any updates. | `scriptablemc.version` |
-##### ScritableMC JavaScript Sub Command
+##### ScritableMC JavaScript Engine Commands
| Command | Alias | Description | Permission |
| ------------- | ------------- |-----------------------------------------------------| ------------------------ |
-| `/scriptablemc reload` | `/smc rl /jsrl` | Fully reloads the script engine and all script files. | `scriptablemc.reload` |
+| `/scriptablemc javascript reload` | `/smc rl /jsrl` | Fully reloads the javascript engine and all script files. | `scriptablemc.js.reload` |
| `/scriptablemc javascript execute ` | `/smc js ex ` | Executes javascript source and prints the return value. | `scriptablemc.js.execute` |
| `/scriptablemc javascript execute stash` | `/smc js ex stash` | Executes javascript source stored in your stash. | `scriptablemc.js.execute` |
| `/scriptablemc javascript file ` | `/smc js f ` | Executes javascript file from the scripts folder. | `scriptablemc.js.execute.file` |
@@ -41,6 +45,17 @@ You can take the typescript example and compile it, then directly modify the jav
| `/scriptablemc javascript stash ` | `/smc js st ` | Adds a line to your javascript stash. | `scriptablemc.js.execute` |
| `/scriptablemc javascript stash clear` | `/smc js st clear` | Clears your javascript stash. | `scriptablemc.js.execute` |
+##### ScritableMC Python Engine Commands
+| Command | Alias | Description | Permission |
+| ------------- | ------------- |-----------------------------------------------------| ------------------------ |
+| `/scriptablemc python reload` | `/smc rl /pyrl` | Fully reloads the python script engine and all script files. | `scriptablemc.python.reload` |
+| `/scriptablemc python execute ` | `/smc py ex /pyex` | Executes python source and prints the return value. | `scriptablemc.python.execute` |
+| `/scriptablemc python execute stash` | `/smc py ex stash` | Executes python source stored in your stash. | `scriptablemc.python.execute` |
+| `/scriptablemc python file ` | `/smc py f ` | Executes python file from the scripts folder. | `scriptablemc.python.execute.file` |
+| `/scriptablemc python stash` | `/smc py st` | Prints all stored python lines in your stash. | `scriptablemc.python.execute` |
+| `/scriptablemc python stash ` | `/smc py st ` | Adds a line to your python stash. | `scriptablemc.python.execute` |
+| `/scriptablemc python stash clear` | `/smc py st clear` | Clears your python stash. | `scriptablemc.python.execute` |
+
### `/smc javascript execute` Command Example
> `/jsex return 1 + 1`
> 
@@ -54,4 +69,5 @@ https://github.com/MinusKube/SmartInvs - Inventory helper library
https://github.com/lucko/helper - MinecraftVersions helper library
https://github.com/jkcclemens/khttp - HTTP helper library
https://github.com/aikar/commands - Built-in commands and auto-completion
+https://github.com/apache/commons-io - FileUtils helper library
https://github.com/graalvm/graaljs - JavaScript engine
diff --git a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/ScriptEngineConfig.kt b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/ScriptEngineConfig.kt
new file mode 100644
index 00000000..c3fad357
--- /dev/null
+++ b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/ScriptEngineConfig.kt
@@ -0,0 +1,70 @@
+package com.pixlfox.scriptablemc
+
+import org.bukkit.configuration.file.FileConfiguration
+
+abstract class ScriptEngineConfig(private val config: FileConfiguration) {
+ val rootScriptsFolder: String
+ get() = readConfigString("root_scripts_folder", "./scripts")
+
+ abstract val mainScriptFiles: List
+ abstract val executeCommandTemplate: String
+ abstract val scriptMimeType: String
+
+ val autoEnablePlugins: Boolean
+ get() = readConfigBoolean("auto_enable_plugins", true)
+
+ val debug: Boolean
+ get() = readConfigBoolean("debug", false)
+
+ val extractLibs: Boolean
+ get() = readConfigBoolean("extract_libs", true)
+
+ val debugger: ScriptEngineDebuggerConfig
+ get() = ScriptEngineDebuggerConfig(this)
+
+ @JvmOverloads
+ fun readConfigString(path: String, def: String = ""): String {
+ val input = config.getString(path, def).orEmpty()
+ val regex = Regex("\\\$\\{(.*)}")
+
+ return regex.replace(input) {
+ val configValue = readConfigString(it.groups[1]!!.value)
+ configValue
+ }
+ }
+
+ @JvmOverloads
+ fun readConfigStringList(path: String, def: List = listOf()): List {
+ val inputList = config.getStringList(path)
+ val regex = Regex("\\\$\\{(.*)}")
+
+ if(inputList.isEmpty()) {
+ inputList.addAll(def)
+ }
+
+ for((index, input) in inputList.withIndex()) {
+ inputList[index] = regex.replace(input) {
+ val configValue = readConfigString(it.groups[1]!!.value)
+ configValue
+ }
+ }
+
+ return inputList
+ }
+
+ @JvmOverloads
+ fun readConfigBoolean(path: String, def: Boolean = false): Boolean {
+ return readConfigString(path, def.toString()).equals("true", true)
+ }
+}
+
+class ScriptEngineDebuggerConfig(private val config: ScriptEngineConfig) {
+ val enabled: Boolean
+ get() = config.readConfigBoolean("debugger.enabled", false)
+
+ val address: String
+ get() = config.readConfigString("debugger.address", "127.0.0.1:9229")
+
+ val waitAttached: Boolean
+ get() = config.readConfigBoolean("debugger.wait_attached", true)
+}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
index 47220277..1e0ff017 100644
--- a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
+++ b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
@@ -29,6 +29,9 @@ abstract class ScriptablePluginContext: Listener {
abstract val inventoryManager: InventoryManager
abstract val pluginVersion: Version
+ var isEnabled: Boolean = false
+ internal set
+
val server: Server
get() = Bukkit.getServer()
diff --git a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
index 9796e1be..9c8ea70e 100644
--- a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
+++ b/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
@@ -1,14 +1,20 @@
package com.pixlfox.scriptablemc.core
+import com.pixlfox.scriptablemc.ScriptEngineConfig
import com.pixlfox.scriptablemc.ScriptEngineMain
+import com.smc.exceptions.ScriptNotFoundException
import com.smc.version.Version
import fr.minuskube.inv.InventoryManager
+import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.graalvm.polyglot.*
import java.io.File
+import java.util.*
@Suppress("MemberVisibilityCanBePrivate", "unused")
abstract class ScriptablePluginEngine {
+ abstract val languageName: String
+ abstract val languageFileExtension: String
abstract val bootstrapPlugin: ScriptEngineMain
abstract val debugEnabled: Boolean
@@ -18,16 +24,44 @@ abstract class ScriptablePluginEngine {
abstract val graalContext: Context
abstract val globalBindings: Value
+ abstract val config: ScriptEngineConfig
+
val pluginVersion: Version
get() = bootstrapPlugin.pluginVersion
- abstract fun start()
- abstract fun close()
+ var enabledAllPlugins: Boolean = false
+ internal set
+
+ val startupErrors: MutableList = mutableListOf()
+
+ open fun start() {
+ inventoryManager.init()
+ globalBindings.putMember("engine", this)
+
+ loadAllHelperClasses()
+
+ for(mainScriptFile in config.mainScriptFiles) {
+ loadMainScript(mainScriptFile)
+ }
+
+ if(!enabledAllPlugins && config.autoEnablePlugins) {
+ enableAllPlugins()
+ }
+ }
+
+ open fun close() {
+ for(scriptablePlugin in scriptablePlugins) {
+ scriptablePlugin.disable()
+ }
+ scriptablePlugins.clear()
+
+ graalContext.close(true)
+ }
- fun loadAllHelperClasses() {
+ open fun loadAllHelperClasses() {
for(helperClass in preLoadClasses) {
try {
- javaClass.classLoader.loadClass(helperClass.replace("*", ""))
+ javaClass.classLoader.loadClass(if(helperClass.startsWith("*")) helperClass.substring(1) else helperClass)
}
catch (e: Exception) {
if(!helperClass.startsWith("*")) {
@@ -38,15 +72,109 @@ abstract class ScriptablePluginEngine {
}
}
- abstract fun evalFile(filePath: String): Value
- abstract fun evalFile(scriptFile: File): Value
- abstract fun eval(source: String): Value
- abstract fun evalCommandSender(source: String, sender: CommandSender): Value
- abstract fun eval(source: Source): Value
+ open fun enableAllPlugins() {
+ for (pluginContext in scriptablePlugins.filter { !it.isEnabled }) {
+ enablePlugin(pluginContext)
+ }
+ enabledAllPlugins = true
+ }
+
+ open fun disableAllPlugins() {
+ for (pluginContext in scriptablePlugins.filter { it.isEnabled }) {
+ disablePlugin(pluginContext)
+ }
+ enabledAllPlugins = false
+ }
+
+ open fun enablePlugin(pluginContext: ScriptablePluginContext) {
+ if(!pluginContext.isEnabled) {
+ pluginContext.enable()
+ }
+ else {
+ bootstrapPlugin.logger.warning("Trying to enable an already-enabled scriptable plugin context.")
+ }
+ }
+
+ open fun disablePlugin(pluginContext: ScriptablePluginContext) {
+ if(pluginContext.isEnabled) {
+ pluginContext.disable()
+ }
+ else {
+ bootstrapPlugin.logger.warning("Trying to disabled an already-disabled scriptable plugin context.")
+ }
+ }
+
+ open fun eval(source: Source): Value {
+ return graalContext.eval(source)
+ }
+
+ open fun evalCommandSender(source: String, sender: CommandSender): Value {
+ val tempScriptFile = File("${config.rootScriptsFolder}/${UUID.randomUUID()}.$languageFileExtension")
+ try {
+ tempScriptFile.writeText(config.executeCommandTemplate.replace("%SOURCE%", source))
+
+ var evalReturn = evalFile(tempScriptFile)
+
+ if(evalReturn.canInstantiate()) {
+ evalReturn = evalReturn.newInstance()
+ }
+
+ if(evalReturn.hasMember("execute") && evalReturn.canInvokeMember("execute")) {
+ evalReturn.putMember("sender", sender)
+ evalReturn.putMember("server", Bukkit.getServer())
+ evalReturn.putMember("servicesManager", Bukkit.getServicesManager())
+ return evalReturn.invokeMember("execute", sender, Bukkit.getServer(), Bukkit.getServicesManager())
+ }
+
+ return evalReturn
+ }
+ finally {
+ tempScriptFile.delete()
+ }
+ }
+
+ open fun evalFile(filePath: String): Value {
+ val scriptFile = File("${config.rootScriptsFolder}/$filePath")
+
+ return if(scriptFile.exists()) {
+ eval(
+ Source.newBuilder(languageName, scriptFile)
+ .name(scriptFile.name)
+ .mimeType(config.scriptMimeType)
+ .interactive(false)
+ .build()
+ )
+ } else {
+ throw ScriptNotFoundException(scriptFile)
+ }
+ }
+
+ open fun evalFile(scriptFile: File): Value {
+ return if(scriptFile.exists()) {
+ eval(
+ Source.newBuilder(languageName, scriptFile)
+ .name(scriptFile.name)
+ .mimeType(config.scriptMimeType)
+ .interactive(false)
+ .build()
+ )
+ } else {
+ throw ScriptNotFoundException(scriptFile)
+ }
+ }
+
+ open fun eval(source: String): Value {
+ return graalContext.eval(
+ Source.newBuilder(languageName, source,"${UUID.randomUUID()}.$languageFileExtension")
+ .mimeType(config.scriptMimeType)
+ .interactive(false)
+ .cached(false)
+ .build()
+ )
+ }
+
+ abstract fun loadMainScript(path: String)
abstract fun loadPlugin(scriptableClass: Value): ScriptablePluginContext
- abstract fun enableAllPlugins()
- abstract fun enablePlugin(pluginContext: ScriptablePluginContext)
- abstract fun disablePlugin(pluginContext: ScriptablePluginContext)
companion object {
val preLoadClasses: Array = arrayOf(
diff --git a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/exceptions/ScriptNotFoundException.kt b/ScriptableMC-Engine-Core/src/main/kotlin/com/smc/exceptions/ScriptNotFoundException.kt
similarity index 50%
rename from ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/exceptions/ScriptNotFoundException.kt
rename to ScriptableMC-Engine-Core/src/main/kotlin/com/smc/exceptions/ScriptNotFoundException.kt
index b49568bb..9487a94e 100644
--- a/ScriptableMC-Engine-Core/src/main/kotlin/com/pixlfox/scriptablemc/exceptions/ScriptNotFoundException.kt
+++ b/ScriptableMC-Engine-Core/src/main/kotlin/com/smc/exceptions/ScriptNotFoundException.kt
@@ -1,5 +1,5 @@
-package com.pixlfox.scriptablemc.exceptions
+package com.smc.exceptions
import java.io.File
-class ScriptNotFoundException(scriptFile: File) : Exception("Unable to load script: ${scriptFile.path}.")
\ No newline at end of file
+class ScriptNotFoundException(scriptFile: File) : Exception("Unable to load script: ${scriptFile.absolutePath}.")
\ No newline at end of file
diff --git a/ScriptableMC-Engine-JS-Bundled/build.gradle.kts b/ScriptableMC-Engine-JS/Bundled/build.gradle.kts
similarity index 100%
rename from ScriptableMC-Engine-JS-Bundled/build.gradle.kts
rename to ScriptableMC-Engine-JS/Bundled/build.gradle.kts
diff --git a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptConfig.kt b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptConfig.kt
index 7abdadc3..d4b2f1bb 100644
--- a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptConfig.kt
+++ b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptConfig.kt
@@ -2,27 +2,27 @@ package com.pixlfox.scriptablemc
import org.bukkit.configuration.file.FileConfiguration
-class SMCJavaScriptConfig(private val config: FileConfiguration) {
- val rootScriptsFolder: String
- get() = config.getString("root_scripts_folder", "./scripts").orEmpty()
+class SMCJavaScriptConfig(config: FileConfiguration) : ScriptEngineConfig(config) {
+ override val mainScriptFiles: List
+ get() = readConfigStringList("main_script_files", listOf("\${root_scripts_folder}/main.js"))
- val debug: Boolean
- get() = config.getBoolean("debug", false)
+ override val executeCommandTemplate: String
+ get() = readConfigString("execute_command_template", "import * as lib from './lib/global.js';\n" +
+ "new (class EvalCommandSenderContext {\n" +
+ " execute(sender, server, servicesManager) {\n" +
+ " %SOURCE%\n" +
+ " }\n" +
+ "})()")
- val extractLibs: Boolean
- get() = config.getBoolean("extract_libs", true)
+ override val scriptMimeType: String
+ get() = readConfigString("script_mime_type", "application/javascript+module")
- val debugger: SMCJavaScriptDebuggerConfig
- get() = SMCJavaScriptDebuggerConfig(config)
-}
+ val commonJsModulesEnabled: Boolean
+ get() = readConfigBoolean("common_js.modules_enabled", true)
-class SMCJavaScriptDebuggerConfig(private val config: FileConfiguration) {
- val enabled: Boolean
- get() = config.getBoolean("debugger.enabled", false)
+ val commonJsModulesPath: String
+ get() = readConfigString("common_js.modules_path", "\${root_scripts_folder}/node_modules")
- val address: String
- get() = config.getString("debugger.address", "127.0.0.1:9229").orEmpty()
-
- val waitAttached: Boolean
- get() = config.getBoolean("debugger.wait_attached", true)
+ val commonJsGlobalsFile: String
+ get() = readConfigString("common_js.globals_file", "globals.js")
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptEngineMain.kt b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptEngineMain.kt
index 8fb0c127..4521c402 100644
--- a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptEngineMain.kt
+++ b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/SMCJavaScriptEngineMain.kt
@@ -53,47 +53,49 @@ class SMCJavaScriptEngineMain : ScriptEngineMain() {
try {
scriptEngine = JavaScriptPluginEngine(this, SMCJavaScriptConfig(config))
scriptEngine!!.start()
- logger.info("JavaScript engine started.")
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.GREEN}JavaScript engine started.")
- } catch (e: IllegalStateException) {
- if (e.message?.contains("Make sure the truffle-api.jar is on the classpath.", true) == true) {
- logger.warning("JavaScript engine failed to start.")
- e.printStackTrace()
- logger.severe("Unable to find truffle-api.jar. This shouldn't happen since it's now packaged inside the plugin, you might want to check for an updated ScriptableMC Engine release.")
- logger.severe("ScriptableMC Engine Version: $pluginVersion")
- logger.severe("ScriptableMC-Engine Download Page: https://github.com/astorks/ScriptableMC-Engine/releases/latest")
-
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}JavaScript engine failed to start. Check the server console.")
- } else {
- logger.warning("JavaScript engine failed to start.")
- e.printStackTrace()
+ if(scriptEngine!!.startupErrors.any()) {
+ for(error in scriptEngine!!.startupErrors) {
+ error.printStackTrace()
+ if(sender != null) {
+ sender.sendMessage("$chatMessagePrefix ${ChatColor.RED}$error")
+ for (stackTrace in error.stackTrace.filter { it.fileName?.endsWith(".js") == true }) {
+ sender.sendMessage("${ChatColor.RED}$stackTrace")
+ }
+ }
+ }
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}JavaScript engine failed to start. Check the server console.")
+ logger.warning("JavaScript engine started with errors.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.YELLOW}JavaScript engine started with errors.")
}
- } catch (e: Exception) {
- logger.warning("JavaScript engine failed to start.")
- e.printStackTrace()
-
+ else {
+ logger.info("JavaScript engine started.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.GREEN}JavaScript engine started.")
+ }
+ }
+ catch (error: Exception) {
+ error.printStackTrace()
if(sender != null) {
- sender.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}$e")
- for (stackTrace in e.stackTrace) {
- sender.sendMessage("${ChatColor.DARK_RED}$stackTrace")
+ sender.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}$error")
+ for (stackTrace in error.stackTrace) {
+ sender.sendMessage("${ChatColor.RED}$stackTrace")
}
}
+
+ logger.severe("JavaScript engine failed to start.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}JavaScript engine failed to start.")
}
}
}
override fun reloadScriptEngine(sender: CommandSender?) {
unloadScriptEngine(sender)
+ saveDefaultConfig()
reloadConfig()
loadScriptEngine(sender)
}
companion object {
- private var inst: SMCJavaScriptEngineMain? = null
- var instance: SMCJavaScriptEngineMain
- internal set(value) { inst = value }
- get() { return inst!! }
+ var instance: SMCJavaScriptEngineMain? = null
+ private set
}
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginContext.kt b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginContext.kt
index 8d5cd585..84988681 100644
--- a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginContext.kt
+++ b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginContext.kt
@@ -28,6 +28,7 @@ class JavaScriptPluginContext(override val engine: ScriptablePluginEngine, overr
}
pluginInstance.invokeMember("onEnable")
+ isEnabled = true
}
override fun disable() {
@@ -43,6 +44,7 @@ class JavaScriptPluginContext(override val engine: ScriptablePluginEngine, overr
for(command in commands) {
unregisterCommand(command)
}
+ isEnabled = false
}
companion object {
diff --git a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginEngine.kt b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginEngine.kt
index 99e1ddeb..3baf2458 100644
--- a/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginEngine.kt
+++ b/ScriptableMC-Engine-JS/src/main/kotlin/com/pixlfox/scriptablemc/core/JavaScriptPluginEngine.kt
@@ -2,24 +2,22 @@ package com.pixlfox.scriptablemc.core
import com.pixlfox.scriptablemc.SMCJavaScriptConfig
import com.pixlfox.scriptablemc.ScriptEngineMain
-import com.pixlfox.scriptablemc.exceptions.ScriptNotFoundException
+import com.smc.exceptions.ScriptNotFoundException
import com.pixlfox.scriptablemc.utils.UnzipUtility
import fr.minuskube.inv.InventoryManager
-import org.bukkit.Bukkit
-import org.bukkit.command.CommandSender
import org.graalvm.polyglot.*
import java.io.File
-import java.util.*
@Suppress("MemberVisibilityCanBePrivate", "unused")
-class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, private val config: SMCJavaScriptConfig): ScriptablePluginEngine() {
+class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, override val config: SMCJavaScriptConfig): ScriptablePluginEngine() {
+ override val languageName: String = "js"
+ override val languageFileExtension: String = "js"
override val debugEnabled: Boolean = config.debug
override val graalContext: Context
override val globalBindings: Value
override val scriptablePlugins: MutableList = mutableListOf()
override val inventoryManager: InventoryManager = InventoryManager(bootstrapPlugin)
- private var enabledAllPlugins: Boolean = false
init {
if(config.extractLibs) {
@@ -34,7 +32,7 @@ class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, pri
}
var contextBuilder = Context
- .newBuilder("js")
+ .newBuilder(languageName)
.allowAllAccess(true)
.allowExperimentalOptions(true)
.allowHostAccess(HostAccess.ALL)
@@ -43,17 +41,41 @@ class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, pri
.allowCreateThread(true)
.option("js.ecmascript-version", "2020")
- contextBuilder = contextBuilder
- .option("js.commonjs-require", "true")
+ if(config.commonJsModulesEnabled) {
+ if(config.debug) {
+ bootstrapPlugin.logger.info("Enabling CommonJS support...")
+ }
- if(File("${config.rootScriptsFolder}/node_modules/").exists()) {
- contextBuilder = contextBuilder
- .option("js.commonjs-require-cwd", "${config.rootScriptsFolder}/node_modules")
- }
+ if (!File(config.commonJsModulesPath).exists()) {
+ if(config.debug) {
+ bootstrapPlugin.logger.info("CommonJS creating modules folder: ${config.commonJsModulesPath}")
+ }
+ File(config.commonJsModulesPath).mkdirs()
+ }
+ else {
+ if(config.debug) {
+ bootstrapPlugin.logger.info("CommonJS using modules folder: ${config.commonJsModulesPath}")
+ }
+ }
- if(File("${config.rootScriptsFolder}/__globals__.js").exists()) {
contextBuilder = contextBuilder
- .option("js.commonjs-global-properties", "${config.rootScriptsFolder}/__globals__.js")
+ .option("js.commonjs-require", "true")
+ .option("js.commonjs-require-cwd", config.commonJsModulesPath)
+
+ if (File(File(config.commonJsModulesPath), config.commonJsGlobalsFile).exists()) {
+ if(config.debug) {
+ bootstrapPlugin.logger.info("CommonJS using globals: ${File(File(config.commonJsModulesPath), config.commonJsGlobalsFile).path}")
+ }
+ contextBuilder = contextBuilder
+ .option("js.commonjs-global-properties", config.commonJsGlobalsFile)
+ }
+ else {
+ if(config.debug) {
+ bootstrapPlugin.logger.warning("CommonJS unable to read globals: ${File(File(config.commonJsModulesPath), config.commonJsGlobalsFile).path}")
+ }
+ }
+
+ bootstrapPlugin.logger.info("CommonJS support enabled.")
}
if(config.debugger.enabled) {
@@ -67,120 +89,49 @@ class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, pri
graalContext = contextBuilder.build()
- globalBindings = graalContext.getBindings("js")
+ globalBindings = graalContext.getBindings(languageName)
}
- override fun start() {
- instance = this
- inventoryManager.init()
- globalBindings.putMember("engine", this)
-
- loadAllHelperClasses()
-
- val mainScriptFile = File("${config.rootScriptsFolder}/main.js")
- if(!mainScriptFile.parentFile.exists()) {
- mainScriptFile.parentFile.mkdirs()
- }
-
- if(mainScriptFile.exists()) {
- val mainReturn = eval(
- Source.newBuilder("js", mainScriptFile)
- .name("main.js")
- .mimeType("application/javascript+module")
- .interactive(false)
- .build()
- )
-
- // Load all plugin types returned as an array
- if(mainReturn.hasArrayElements()) {
- for (i in 0 until mainReturn.arraySize) {
- this.loadPlugin(mainReturn.getArrayElement(i))
- }
+ override fun loadMainScript(path: String) {
+ try {
+ val mainScriptFile = File(path)
+ if(!mainScriptFile.parentFile.exists()) {
+ mainScriptFile.parentFile.mkdirs()
+ }
- // Enable all plugins if not already enabled
- if(!enabledAllPlugins) {
- enableAllPlugins()
+ if(mainScriptFile.exists()) {
+ val mainReturn = eval(
+ Source.newBuilder(languageName, mainScriptFile)
+ .name(mainScriptFile.name)
+ .mimeType(config.scriptMimeType)
+ .interactive(false)
+ .build()
+ )
+
+ // Load all plugin types returned as an array
+ if(mainReturn.hasArrayElements()) {
+ for (i in 0 until mainReturn.arraySize) {
+ this.loadPlugin(mainReturn.getArrayElement(i))
+ }
}
}
+ else {
+ throw ScriptNotFoundException(mainScriptFile)
+ }
}
- else {
- throw ScriptNotFoundException(mainScriptFile)
- }
- }
-
- override fun close() {
- instance = null
- for(scriptablePlugin in scriptablePlugins) {
- scriptablePlugin.disable()
- }
- scriptablePlugins.clear()
-
- graalContext.close(true)
- }
-
- override fun evalFile(filePath: String): Value {
- val scriptFile = File("${config.rootScriptsFolder}/$filePath")
-
- return if(scriptFile.exists()) {
- eval(
- Source.newBuilder("js", scriptFile)
- .name(scriptFile.name)
- .mimeType("application/javascript+module")
- .interactive(false)
- .build()
- )
- } else {
- throw ScriptNotFoundException(scriptFile)
+ catch(ex: Exception) {
+ startupErrors.add(ex)
}
}
- override fun evalFile(scriptFile: File): Value {
- return if(scriptFile.exists()) {
- eval(
- Source.newBuilder("js", scriptFile)
- .name(scriptFile.name)
- .mimeType("application/javascript+module")
- .interactive(false)
- .build()
- )
- } else {
- throw ScriptNotFoundException(scriptFile)
- }
- }
-
- override fun eval(source: String): Value {
- return graalContext.eval(
- Source.newBuilder("js", source,"${UUID.randomUUID()}.js")
- .mimeType("application/javascript+module")
- .interactive(false)
- .cached(false)
- .build()
- )
- }
-
- override fun evalCommandSender(source: String, sender: CommandSender): Value {
- val tempScriptFile = File("${config.rootScriptsFolder}/${UUID.randomUUID()}.js")
- try {
- tempScriptFile.writeText("import * as lib from './lib/global.js';\n" +
- "new (class EvalCommandSenderContext {\n" +
- " run(sender, server, servicesManager) {\n" +
- " $source\n" +
- " }\n" +
- "})()\n")
- val evalCommandSenderContext = evalFile(tempScriptFile)
-
- evalCommandSenderContext.putMember("sender", sender)
- evalCommandSenderContext.putMember("server", Bukkit.getServer())
- evalCommandSenderContext.putMember("servicesManager", Bukkit.getServicesManager())
- return evalCommandSenderContext.invokeMember("run", sender, Bukkit.getServer(), Bukkit.getServicesManager())
- }
- finally {
- tempScriptFile.delete()
- }
+ override fun start() {
+ instance = this
+ super.start()
}
- override fun eval(source: Source): Value {
- return graalContext.eval(source)
+ override fun close() {
+ instance = null
+ super.close()
}
override fun loadPlugin(scriptableClass: Value): ScriptablePluginContext {
@@ -193,25 +144,8 @@ class JavaScriptPluginEngine(override val bootstrapPlugin: ScriptEngineMain, pri
return pluginContext
}
- override fun enableAllPlugins() {
- for (pluginContext in scriptablePlugins) {
- pluginContext.enable()
- }
- enabledAllPlugins = true
- }
-
- override fun enablePlugin(pluginContext: ScriptablePluginContext) {
- pluginContext.enable()
- }
-
- override fun disablePlugin(pluginContext: ScriptablePluginContext) {
- pluginContext.disable()
- }
-
companion object {
- private var inst: JavaScriptPluginEngine? = null
- var instance: JavaScriptPluginEngine?
- internal set(value) { inst = value }
- get() { return inst }
+ var instance: JavaScriptPluginEngine? = null
+ private set
}
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-JS/src/main/resources/config.yml b/ScriptableMC-Engine-JS/src/main/resources/config.yml
index ebd32987..995f98ac 100644
--- a/ScriptableMC-Engine-JS/src/main/resources/config.yml
+++ b/ScriptableMC-Engine-JS/src/main/resources/config.yml
@@ -3,6 +3,24 @@
# Default: ./scripts
root_scripts_folder: './scripts'
+# Script MIME type
+# ES2020 Module: application/javascript+module
+# Pure JavaScript: application/javascript
+# Default: application/javascript+module
+script_mime_type: 'application/javascript+module'
+
+# Main script files
+# The main entry point javascript files
+# Default: ${root_scripts_folder}/main.js
+main_script_files:
+ - '${root_scripts_folder}/main.js'
+
+# Auto-Enable all scriptable plugins after executing all main scripts
+# If set to false you will have to manually enable all plugins
+# via engine.enableAllPlugins() after you have loaded all plugins
+# Default: true
+auto_enable_plugins: true
+
# Prints some additional debugging messages
# Default: false
debug: false
@@ -18,6 +36,48 @@ version_check: true
# Default: true
extract_libs: true
+# Execute command template
+# This is the code that will be executed with /jsex and /smc javascript execute
+# If a class is returned it will be instantiated and continued on as an object
+# If object is returned and has a method execute script engine will call
+# returnedObject.execute(sender: CommandSender, server: BukkitServer, servicesManager)
+# as well as set returnedObject.sender, returnedObject.server, and returnedObject.servicesManager
+# you can also add additional methods, vars to the template source below
+# Otherwise the script engine will return last execute code
+# %SOURCE% will be replaced with with the command source.
+execute_command_template: "
+import * as lib from './lib/global.js';
+
+new (class EvalCommandSenderContext {
+ execute(sender, server, servicesManager) {
+ %SOURCE%
+ }
+})()"
+
+common_js:
+ # Enable CommonJS `require()` support
+ # Allows the use of CommonJS NPM modules
+ # Default: true
+ enabled: true
+
+ # CommonJS modules path
+ # The default path to load CommonJS modules from.
+ # Default: ${root_scripts_folder}/node_modules
+ modules_path: '${root_scripts_folder}/node_modules'
+
+ # CommonJS globals file name
+ # The CommonJS globals file used to define global CommonJS values
+ # Relative to the `common_js.modules_path` folder
+ # Default: globals.js
+ globals_file: 'globals.js'
+
+# Chrome debugger settings
+# **WARNING** THE DEBUGGER SHOULD NEVER BE ENABLED ON A PUBLIC SERVER
+# IT IS INTENDED ONLY FOR DEBUGGING THE SCRIPT ENGINE AND CAN BREAK/FREEZE YOUR WHOLE SERVER
+# Default: enabled: false, address: 127.0.0.1:9229, wait_attached: true
+# If enabled: true and wait_attached: true the whole minecraft server will freeze until the chrome debugger is attached
+# this is useful for debugging the script engine startup
+# To enable remote debugging set address: 0.0.0.0:9229
debugger:
enabled: false
address: 127.0.0.1:9229
diff --git a/ScriptableMC-Engine-JS/src/main/resources/plugin.yml b/ScriptableMC-Engine-JS/src/main/resources/plugin.yml
index a019cdc1..40b1e83a 100644
--- a/ScriptableMC-Engine-JS/src/main/resources/plugin.yml
+++ b/ScriptableMC-Engine-JS/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
name: ScriptableMC-Engine-JS
description: 'Run JavaScript/TypeScript plugins for Bukkit/Spigot/Paper Minecraft 1.15'
-version: '1.2.0'
+version: '1.2.1'
api-version: '1.13'
author: AStorks
main: com.pixlfox.scriptablemc.SMCJavaScriptEngineMain
diff --git a/ScriptableMC-Engine-PY-Bundled/build.gradle.kts b/ScriptableMC-Engine-PY/Bundled/build.gradle.kts
similarity index 100%
rename from ScriptableMC-Engine-PY-Bundled/build.gradle.kts
rename to ScriptableMC-Engine-PY/Bundled/build.gradle.kts
diff --git a/ScriptableMC-Engine-PY-Bundled/libraries/graalpython.jar b/ScriptableMC-Engine-PY/Bundled/libraries/graalpython.jar
similarity index 100%
rename from ScriptableMC-Engine-PY-Bundled/libraries/graalpython.jar
rename to ScriptableMC-Engine-PY/Bundled/libraries/graalpython.jar
diff --git a/ScriptableMC-Engine-PY-Bundled/libraries/sulong-api.jar b/ScriptableMC-Engine-PY/Bundled/libraries/sulong-api.jar
similarity index 100%
rename from ScriptableMC-Engine-PY-Bundled/libraries/sulong-api.jar
rename to ScriptableMC-Engine-PY/Bundled/libraries/sulong-api.jar
diff --git a/ScriptableMC-Engine-PY-Bundled/libraries/sulong.jar b/ScriptableMC-Engine-PY/Bundled/libraries/sulong.jar
similarity index 100%
rename from ScriptableMC-Engine-PY-Bundled/libraries/sulong.jar
rename to ScriptableMC-Engine-PY/Bundled/libraries/sulong.jar
diff --git a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonConfig.kt b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonConfig.kt
index 4ce0675d..a2d45f0d 100644
--- a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonConfig.kt
+++ b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonConfig.kt
@@ -2,27 +2,13 @@ package com.pixlfox.scriptablemc
import org.bukkit.configuration.file.FileConfiguration
-class SMCPythonConfig(private val config: FileConfiguration) {
- val rootScriptFolder: String
- get() = config.getString("root_scripts_folder", "./scripts").orEmpty()
+class SMCPythonConfig(config: FileConfiguration) : ScriptEngineConfig(config) {
+ override val mainScriptFiles: List
+ get() = readConfigStringList("main_script_files", listOf("\${root_scripts_folder}/main.py"))
- val debug: Boolean
- get() = config.getBoolean("debug", false)
+ override val executeCommandTemplate: String
+ get() = readConfigString("execute_command_template", "")
- val extractLibs: Boolean
- get() = config.getBoolean("extract_libs", true)
-
- val debugger: SMCPythonDebuggerConfig
- get() = SMCPythonDebuggerConfig(config)
-}
-
-class SMCPythonDebuggerConfig(private val config: FileConfiguration) {
- val enabled: Boolean
- get() = config.getBoolean("debugger.enabled", false)
-
- val address: String
- get() = config.getString("debugger.address", "127.0.0.1:9229").orEmpty()
-
- val waitAttached: Boolean
- get() = config.getBoolean("debugger.wait_attached", true)
+ override val scriptMimeType: String
+ get() = readConfigString("script_mime_type", "text/x-python")
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonEngineMain.kt b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonEngineMain.kt
index 3de2c363..a06bf421 100644
--- a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonEngineMain.kt
+++ b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/SMCPythonEngineMain.kt
@@ -53,47 +53,50 @@ class SMCPythonEngineMain : ScriptEngineMain() {
try {
scriptEngine = PythonPluginEngine(this, SMCPythonConfig(config))
scriptEngine!!.start()
- logger.info("Python engine started.")
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.GREEN}Python engine started.")
- } catch (e: IllegalStateException) {
- if (e.message?.contains("Make sure the truffle-api.jar is on the classpath.", true) == true) {
- logger.warning("Python engine failed to start.")
- e.printStackTrace()
- logger.severe("Unable to find truffle-api.jar. This shouldn't happen since it's now packaged inside the plugin, you might want to check for an updated ScriptableMC Engine release.")
- logger.severe("ScriptableMC Engine Version: $pluginVersion")
- logger.severe("ScriptableMC-Engine Download Page: https://github.com/astorks/ScriptableMC-Engine/releases/latest")
-
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}Python engine failed to start. Check the server console.")
- } else {
- logger.warning("Scriptable plugin engine failed to start.")
- e.printStackTrace()
-
- sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}Python engine failed to start. Check the server console.")
- }
- } catch (e: Exception) {
- logger.warning("Python engine failed to start.")
- e.printStackTrace()
+ if(scriptEngine!!.startupErrors.any()) {
+ for(error in scriptEngine!!.startupErrors) {
+ error.printStackTrace()
+ if(sender != null) {
+ sender.sendMessage("$chatMessagePrefix ${ChatColor.RED}$error")
+ for (stackTrace in error.stackTrace.filter { it.fileName?.endsWith(".py") == true }) {
+ sender.sendMessage("${ChatColor.RED}$stackTrace")
+ }
+ }
+ }
+
+ logger.warning("Python engine started with errors.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.YELLOW}Python engine started with errors.")
+ }
+ else {
+ logger.info("Python engine started.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.GREEN}Python engine started.")
+ }
+ }
+ catch (error: Exception) {
+ error.printStackTrace()
if(sender != null) {
- sender.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}$e")
- for (stackTrace in e.stackTrace) {
- sender.sendMessage("${ChatColor.DARK_RED}$stackTrace")
+ sender.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}$error")
+ for (stackTrace in error.stackTrace) {
+ sender.sendMessage("${ChatColor.RED}$stackTrace")
}
}
+
+ logger.severe("Python engine failed to start.")
+ sender?.sendMessage("$chatMessagePrefix ${ChatColor.DARK_RED}Python engine failed to start.")
}
}
}
override fun reloadScriptEngine(sender: CommandSender?) {
unloadScriptEngine(sender)
+ saveDefaultConfig()
reloadConfig()
loadScriptEngine(sender)
}
companion object {
- private var inst: SMCPythonEngineMain? = null
- var instance: SMCPythonEngineMain
- internal set(value) { inst = value }
- get() { return inst!! }
+ var instance: SMCPythonEngineMain? = null
+ private set
}
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginContext.kt b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginContext.kt
index f82452e8..8b9e1b87 100644
--- a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginContext.kt
+++ b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginContext.kt
@@ -28,6 +28,7 @@ class PythonPluginContext(override val engine: ScriptablePluginEngine, override
}
pluginInstance.invokeMember("onEnable")
+ isEnabled = true
}
override fun disable() {
@@ -43,6 +44,7 @@ class PythonPluginContext(override val engine: ScriptablePluginEngine, override
for(command in commands) {
unregisterCommand(command)
}
+ isEnabled = false
}
companion object {
diff --git a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginEngine.kt b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginEngine.kt
index 00e42d5d..22f77fa2 100644
--- a/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginEngine.kt
+++ b/ScriptableMC-Engine-PY/src/main/kotlin/com/pixlfox/scriptablemc/core/PythonPluginEngine.kt
@@ -2,27 +2,26 @@ package com.pixlfox.scriptablemc.core
import com.pixlfox.scriptablemc.SMCPythonConfig
import com.pixlfox.scriptablemc.ScriptEngineMain
-import com.pixlfox.scriptablemc.exceptions.ScriptNotFoundException
+import com.smc.exceptions.ScriptNotFoundException
import com.pixlfox.scriptablemc.utils.UnzipUtility
import fr.minuskube.inv.InventoryManager
-import org.bukkit.command.CommandSender
import org.graalvm.polyglot.*
import java.io.File
-import java.util.*
@Suppress("MemberVisibilityCanBePrivate", "unused")
-class PythonPluginEngine(override val bootstrapPlugin: ScriptEngineMain, private val config: SMCPythonConfig): ScriptablePluginEngine() {
+class PythonPluginEngine(override val bootstrapPlugin: ScriptEngineMain, override val config: SMCPythonConfig): ScriptablePluginEngine() {
+ override val languageName: String = "python"
+ override val languageFileExtension: String = "py"
override val graalContext: Context
override val debugEnabled: Boolean = config.debug
override val globalBindings: Value
override val scriptablePlugins: MutableList = mutableListOf()
override val inventoryManager: InventoryManager = InventoryManager(bootstrapPlugin)
- private var enabledAllPlugins: Boolean = false
init {
if(config.extractLibs) {
val librariesResource = bootstrapPlugin.getResource("lib-py.zip")
- val libFolder = File("${config.rootScriptFolder}/lib-py")
+ val libFolder = File("${config.rootScriptsFolder}/lib-py")
if (librariesResource != null && !libFolder.exists()) {
if(debugEnabled) {
bootstrapPlugin.logger.info("Extracting python libraries from ScriptableMC-Engine-PY resources to ${libFolder.path}...")
@@ -32,14 +31,14 @@ class PythonPluginEngine(override val bootstrapPlugin: ScriptEngineMain, private
}
var contextBuilder = Context
- .newBuilder("python")
+ .newBuilder(languageName)
.allowAllAccess(true)
.allowExperimentalOptions(true)
.allowHostAccess(HostAccess.ALL)
.allowHostClassLoading(true)
.allowIO(true)
- .option("python.CoreHome", "${config.rootScriptFolder}/lib-py/lib-graalpython/")
- .option("python.StdLibHome", "${config.rootScriptFolder}/lib-py/lib-python/3/")
+ .option("python.CoreHome", "${config.rootScriptsFolder}/lib-py/lib-graalpython/")
+ .option("python.StdLibHome", "${config.rootScriptsFolder}/lib-py/lib-python/3/")
if(config.debugger.enabled) {
contextBuilder = contextBuilder
@@ -51,108 +50,53 @@ class PythonPluginEngine(override val bootstrapPlugin: ScriptEngineMain, private
}
graalContext = contextBuilder.build()
- globalBindings = graalContext.getBindings("python")
+ globalBindings = graalContext.getBindings(languageName)
}
- override fun start() {
- instance = this
- inventoryManager.init()
- globalBindings.putMember("engine", this)
-
- loadAllHelperClasses()
-
- val mainScriptFile = File("${config.rootScriptFolder}/main.py")
- if(!mainScriptFile.parentFile.exists()) {
- mainScriptFile.parentFile.mkdirs()
- }
-
- if(mainScriptFile.exists()) {
- val mainReturn = eval(
- Source.newBuilder("python", mainScriptFile)
- .name("main.py")
- .interactive(false)
- .build()
- )
-
- // Load all plugin types returned as an array
- if(mainReturn.hasArrayElements()) {
- for (i in 0 until mainReturn.arraySize) {
- this.loadPlugin(mainReturn.getArrayElement(i))
- }
+ override fun loadMainScript(path: String) {
+ try {
+ val mainScriptFile = File(path)
+ if(!mainScriptFile.parentFile.exists()) {
+ mainScriptFile.parentFile.mkdirs()
+ }
- // Enable all plugins if not already enabled
- if(!enabledAllPlugins) {
- enableAllPlugins()
+ if(mainScriptFile.exists()) {
+ val mainReturn = eval(
+ Source.newBuilder(languageName, mainScriptFile)
+ .name(mainScriptFile.name)
+ .interactive(false)
+ .build()
+ )
+
+ // Load all plugin types returned as an array
+ if(mainReturn.hasArrayElements()) {
+ for (i in 0 until mainReturn.arraySize) {
+ this.loadPlugin(mainReturn.getArrayElement(i))
+ }
+
+ // Enable all plugins if not already enabled
+ if(!enabledAllPlugins) {
+ enableAllPlugins()
+ }
}
}
+ else {
+ throw ScriptNotFoundException(mainScriptFile)
+ }
}
- else {
- throw ScriptNotFoundException(mainScriptFile)
- }
- }
-
- override fun close() {
- instance = null
- for(scriptablePlugin in scriptablePlugins) {
- scriptablePlugin.disable()
- }
- scriptablePlugins.clear()
-
- graalContext.close(true)
- }
-
- override fun evalFile(filePath: String): Value {
- val scriptFile = File("${config.rootScriptFolder}/$filePath")
-
- return if(scriptFile.exists()) {
- eval(
- Source.newBuilder("python", scriptFile)
- .name(scriptFile.name)
- .interactive(false)
- .build()
- )
- } else {
- throw ScriptNotFoundException(scriptFile)
- }
- }
-
- override fun evalFile(scriptFile: File): Value {
- return if(scriptFile.exists()) {
- eval(
- Source.newBuilder("python", scriptFile)
- .name(scriptFile.name)
- .interactive(false)
- .build()
- )
- } else {
- throw ScriptNotFoundException(scriptFile)
+ catch(ex: Exception) {
+ startupErrors.add(ex)
}
}
- override fun eval(source: String): Value {
- return graalContext.eval(
- Source.newBuilder("python", source,"${UUID.randomUUID()}.py")
- .interactive(false)
- .cached(false)
- .build()
- )
- }
-
- override fun evalCommandSender(source: String, sender: CommandSender): Value {
- val tempScriptFile = File("${config.rootScriptFolder}/${UUID.randomUUID()}/__init__.py")
- try {
- tempScriptFile.parentFile.mkdirs()
- tempScriptFile.writeText(source)
- return evalFile(tempScriptFile)
- }
- finally {
- tempScriptFile.delete()
- tempScriptFile.parentFile.delete()
- }
+ override fun start() {
+ instance = this
+ super.start()
}
- override fun eval(source: Source): Value {
- return graalContext.eval(source)
+ override fun close() {
+ instance = null
+ super.close()
}
override fun loadPlugin(scriptableClass: Value): ScriptablePluginContext {
@@ -165,25 +109,8 @@ class PythonPluginEngine(override val bootstrapPlugin: ScriptEngineMain, private
return pluginContext
}
- override fun enableAllPlugins() {
- for (pluginContext in scriptablePlugins) {
- pluginContext.enable()
- }
- enabledAllPlugins = true
- }
-
- override fun enablePlugin(pluginContext: ScriptablePluginContext) {
- pluginContext.enable()
- }
-
- override fun disablePlugin(pluginContext: ScriptablePluginContext) {
- pluginContext.disable()
- }
-
companion object {
- private var inst: PythonPluginEngine? = null
- var instance: PythonPluginEngine?
- internal set(value) { inst = value }
- get() { return inst }
+ var instance: PythonPluginEngine? = null
+ private set
}
}
\ No newline at end of file
diff --git a/ScriptableMC-Engine-PY/src/main/resources/config.yml b/ScriptableMC-Engine-PY/src/main/resources/config.yml
index 12ec47ab..bce27697 100644
--- a/ScriptableMC-Engine-PY/src/main/resources/config.yml
+++ b/ScriptableMC-Engine-PY/src/main/resources/config.yml
@@ -3,6 +3,24 @@
# Default: ./scripts
root_scripts_folder: './scripts'
+# Script MIME type
+# .py text/x-python
+# .pyc application/x-python-code
+# Default: text/x-python
+script_mime_type: 'text/x-python'
+
+# Main script files
+# The main entry point python script files
+# Default: ${root_scripts_folder}/main.py
+main_script_files:
+ - '${root_scripts_folder}/main.py'
+
+# Auto-Enable all scriptable plugins after loading main scripts
+# If set to false you will have to manually enable all plugins
+# via engine.enableAllPlugins() after you have loaded all plugins
+# Default: true
+auto_enable_plugins: true
+
# Prints some additional debugging messages
# Default: false
debug: false
@@ -18,6 +36,24 @@ version_check: true
# Default: true
extract_libs: true
+# Execute command template
+# This is the code that will be executed with /pyex and /smc python execute
+# If a class is returned it will be instantiated and continued on as an object
+# If object is returned and has a method execute script engine will call
+# returnedObject.execute(sender: CommandSender, server: BukkitServer, servicesManager)
+# as well as set returnedObject.sender, returnedObject.server, and returnedObject.servicesManager
+# you can also add additional methods, vars to the template source below
+# Otherwise the script engine will return last execute code
+# %SOURCE% will be replaced with with the command source.
+execute_command_template: "%SOURCE%"
+
+# Chrome debugger settings
+# **WARNING** THE DEBUGGER SHOULD NEVER BE ENABLED ON A PUBLIC SERVER
+# IT IS INTENDED ONLY FOR DEBUGGING THE SCRIPT ENGINE AND CAN BREAK/FREEZE YOUR WHOLE SERVER
+# Default: enabled: false, address: 127.0.0.1:9229, wait_attached: true
+# If enabled: true and wait_attached: true the whole minecraft server will freeze until the chrome debugger is attached
+# this is useful for debugging the script engine startup
+# To enable remote debugging set address: 0.0.0.0:9229
debugger:
enabled: false
address: 127.0.0.1:9229
diff --git a/ScriptableMC-Engine-PY/src/main/resources/plugin.yml b/ScriptableMC-Engine-PY/src/main/resources/plugin.yml
index 7480341d..59101626 100644
--- a/ScriptableMC-Engine-PY/src/main/resources/plugin.yml
+++ b/ScriptableMC-Engine-PY/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
name: ScriptableMC-Engine-PY
description: 'Run Python plugins for Bukkit/Spigot/Paper Minecraft 1.15'
-version: '1.2.0'
+version: '1.2.1'
api-version: '1.13'
author: AStorks
main: com.pixlfox.scriptablemc.SMCPythonEngineMain
diff --git a/ScriptableMC-Tools-TS/build.gradle.kts b/ScriptableMC-Tools-TS/build.gradle.kts
index 132204bd..5e3f17a3 100644
--- a/ScriptableMC-Tools-TS/build.gradle.kts
+++ b/ScriptableMC-Tools-TS/build.gradle.kts
@@ -57,5 +57,20 @@ tasks.register("exportTypeScriptLibraries") {
group = "libraries"
main = "com.pixlfox.scriptablemc.TypescriptLibraryExporter"
classpath = sourceSets["main"].runtimeClasspath
- //classpath = sourceSets["main"].java.srcDirs
+}
+
+tasks.register("generateLibSMC") {
+ dependsOn(":ScriptableMC-Tools-TS:classes")
+ group = "libraries"
+ main = "com.pixlfox.scriptablemc.TypescriptLibraryExporter"
+ args = mutableListOf("--lib-smc")
+ classpath = sourceSets["main"].runtimeClasspath
+}
+
+tasks.register("generateLibSMCRelease") {
+ dependsOn(":ScriptableMC-Tools-TS:classes")
+ group = "libraries"
+ main = "com.pixlfox.scriptablemc.TypescriptLibraryExporter"
+ args = mutableListOf("--lib-smc", "--release")
+ classpath = sourceSets["main"].runtimeClasspath
}
\ No newline at end of file
diff --git a/ScriptableMC-Tools-TS/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt b/ScriptableMC-Tools-TS/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
index b7604754..f6d05e41 100644
--- a/ScriptableMC-Tools-TS/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
+++ b/ScriptableMC-Tools-TS/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
@@ -5,16 +5,18 @@ import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import com.thoughtworks.paranamer.BytecodeReadingParanamer
import com.thoughtworks.paranamer.Paranamer
+import org.bukkit.plugin.PluginDescriptionFile
import java.io.File
import java.lang.reflect.*
@Suppress("MemberVisibilityCanBePrivate", "UnstableApiUsage", "unused")
-class TypescriptLibraryExporter {
+class TypescriptLibraryExporter(args: Array = arrayOf()) {
private var basePath: String = "./lib"
private val classList = mutableListOf>()
private var allowedPackagesRegex: Regex = Regex("(org\\.bukkit|com\\.pixlfox|com\\.smc|fr\\.minuskube\\.inv|com\\.google|java\\.sql|java\\.io|java\\.nio|khttp|org\\.apache\\.commons\\.io)(.*)?")
private val paranamer: Paranamer = BytecodeReadingParanamer()
+ private val isRelease: Boolean = args.contains("--release")
private fun safeName(name: String): String = when {
name.equals("function", true) -> "_function"
@@ -349,6 +351,18 @@ class TypescriptLibraryExporter {
return this
}
+ fun exportIndexLibrary(): TypescriptLibraryExporter {
+ val file = File("$basePath/ts/index.ts")
+ if(!file.exists()) {
+ file.parentFile.mkdirs()
+ file.createNewFile()
+ file.writeText(generateTypescriptGlobalExports())
+ println("Exported index.ts.")
+ }
+
+ return this
+ }
+
fun copyStaticSources(): TypescriptLibraryExporter {
File("./src/main/ts/").copyRecursively(File("$basePath/ts/"), true)
@@ -376,7 +390,8 @@ class TypescriptLibraryExporter {
" \"version\": \"1.0.0\",\n" +
" \"description\": \"Typescript plugin example and libraries for Minecraft 1.15\",\n" +
" \"scripts\": {\n" +
- " \"compile\": \"npx tsc\"\n" +
+ " \"compile\": \"npx tsc\",\n" +
+ " \"compile_commonjs\": \"npx tsc -m commonjs --outDir ./commonjs\"\n" +
" },\n" +
" \"author\": \"Ashton Storks\",\n" +
" \"license\": \"ISC\",\n" +
@@ -393,6 +408,66 @@ class TypescriptLibraryExporter {
return this
}
+ fun exportCommonJSProjectFiles(): TypescriptLibraryExporter {
+ File("$basePath/tsconfig.json").writeText("{\n" +
+ " \"compilerOptions\": {\n" +
+ " \"target\": \"es2019\",\n" +
+ " \"module\": \"commonjs\",\n" +
+ " \"sourceMap\": false,\n" +
+ " \"allowJs\": true,\n" +
+ " \"outDir\": \"js\",\n" +
+ " \"rootDir\": \"ts\",\n" +
+ " \"declaration\": true\n" +
+ " },\n" +
+ " \"include\": [\n" +
+ " \"ts/**/*\"\n" +
+ " ]\n" +
+ "}")
+
+ val pluginDescription = PluginDescriptionFile(File("../ScriptableMC-Engine-JS/src/main/resources/plugin.yml").inputStream())
+ val githubSha = System.getenv().getOrElse("GITHUB_SHA") { "" }
+ val githubTag = System.getenv().getOrElse("GITHUB_REF") { "" }
+
+ val isReleaseTag = githubTag.startsWith("refs/tags/v") && isRelease
+
+ val version = when {
+ isReleaseTag -> githubTag.substring(11)
+ isRelease && githubSha.isNullOrEmpty() -> pluginDescription.version
+ else -> "${pluginDescription.version}-dev-$githubSha"
+ }
+
+ if(isRelease) {
+ File("$basePath/.npmrc").writeText("//registry.npmjs.org/:_authToken=\${NPM_TOKEN}")
+ }
+ else {
+ File("$basePath/.npmrc").writeText("//npm.pkg.github.com/:_authToken=\${GITHUB_TOKEN}\n" +
+ "registry=https://npm.pkg.github.com/astorks")
+ }
+
+ File("$basePath/package.json").writeText("{\n" +
+ " \"name\": \"@astorks/lib-smc\",\n" +
+ " \"repository\": \"git@github.com:astorks/ScriptableMC-Engine.git\",\n" +
+ " \"version\": \"$version\",\n" +
+ " \"description\": \"JavaScript CommonJS libraries for ScriptableMC\",\n" +
+ " \"scripts\": {\n" +
+ " \"compile\": \"npx tsc\",\n" +
+ " \"postcompile\": \"cp ./package.json ./js/package.json && cp ./.npmrc ./js/.npmrc\"\n" +
+ " },\n" +
+ " \"author\": \"Ashton Storks\",\n" +
+ " \"license\": \"ISC\",\n" +
+ " \"bugs\": {\n" +
+ " \"url\": \"https://github.com/astorks/ScriptableMC-TypeScript/issues\"\n" +
+ " },\n" +
+ " \"homepage\": \"https://github.com/astorks/ScriptableMC-TypeScript#readme\",\n" +
+ " \"dependencies\": {},\n" +
+ " \"devDependencies\": {\n" +
+ " \"typescript\": \"^3.7.5\"\n" +
+ " }\n" +
+ "}")
+
+ return this
+ }
+
@Suppress("UNCHECKED_CAST")
private fun generateTypescriptSource(_class: Class<*>): String {
var source = "declare var Java: any;\n"
@@ -404,6 +479,7 @@ class TypescriptLibraryExporter {
return source
}
+
private fun generateTypescriptImports(_class: Class<*>): String {
var tsImportsSource = ""
@@ -666,14 +742,27 @@ class TypescriptLibraryExporter {
companion object {
@JvmStatic
fun main(args: Array) {
- TypescriptLibraryExporter()
- .addHelperClasses()
- .addBukkitClasses()
- .clean()
- .exportLibraries()
- .exportGlobalLibrary()
- .copyStaticSources()
- .exportProjectFiles()
+ if(args.contains("--lib-smc")) {
+ TypescriptLibraryExporter(args)
+ .basePath("./lib-smc")
+ .addHelperClasses()
+ .addBukkitClasses()
+ .clean()
+ .exportLibraries()
+ .exportIndexLibrary()
+ .copyStaticSources()
+ .exportCommonJSProjectFiles()
+ }
+ else {
+ TypescriptLibraryExporter(args)
+ .addHelperClasses()
+ .addBukkitClasses()
+ .clean()
+ .exportLibraries()
+ .exportGlobalLibrary()
+ .copyStaticSources()
+ .exportProjectFiles()
+ }
}
}
}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 8d35fd86..e1c0ce13 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,7 +7,7 @@ plugins {
allprojects {
group = "com.pixlfox.scriptablemc"
- version = "1.2.0"
+ version = "1.2.1"
repositories {
mavenCentral()
@@ -47,9 +47,9 @@ tasks.register("shadowJarAll") {
group = "shadow"
dependsOn(":ScriptableMC-Engine-JS:shadowJar")
- dependsOn(":ScriptableMC-Engine-JS-Bundled:shadowJar")
+ dependsOn(":ScriptableMC-Engine-JS:Bundled:shadowJar")
dependsOn(":ScriptableMC-Engine-PY:shadowJar")
- dependsOn(":ScriptableMC-Engine-PY-Bundled:shadowJar")
+ dependsOn(":ScriptableMC-Engine-PY:Bundled:shadowJar")
doFirst {
if(!file("./build").exists()) file("./build").mkdirs()
@@ -61,9 +61,9 @@ tasks.register("shadowJarAll") {
doLast {
file("./ScriptableMC-Engine-JS/build/libs/ScriptableMC-Engine-JS.jar").copyTo(file("./build/ScriptableMC-Engine-JS.jar"), overwrite = true)
- file("./ScriptableMC-Engine-JS-Bundled/build/libs/ScriptableMC-Engine-JS-Bundled.jar").copyTo(file("./build/ScriptableMC-Engine-JS-Bundled.jar"), overwrite = true)
+ file("./ScriptableMC-Engine-JS/Bundled/build/libs/ScriptableMC-Engine-JS-Bundled.jar").copyTo(file("./build/ScriptableMC-Engine-JS-Bundled.jar"), overwrite = true)
file("./ScriptableMC-Engine-PY/build/libs/ScriptableMC-Engine-PY.jar").copyTo(file("./build/ScriptableMC-Engine-PY.jar"), overwrite = true)
- file("./ScriptableMC-Engine-PY-Bundled/build/libs/ScriptableMC-Engine-PY-Bundled.jar").copyTo(file("./build/ScriptableMC-Engine-PY-Bundled.jar"), overwrite = true)
+ file("./ScriptableMC-Engine-PY/Bundled/build/libs/ScriptableMC-Engine-PY-Bundled.jar").copyTo(file("./build/ScriptableMC-Engine-PY-Bundled.jar"), overwrite = true)
}
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index de616a9d..193fbe93 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -2,7 +2,7 @@ rootProject.name = "ScriptableMC-Engine"
include("ScriptableMC-Engine-Core")
include("ScriptableMC-Engine-JS")
-include("ScriptableMC-Engine-JS-Bundled")
+include("ScriptableMC-Engine-JS:Bundled")
include("ScriptableMC-Engine-PY")
-include("ScriptableMC-Engine-PY-Bundled")
+include("ScriptableMC-Engine-PY:Bundled")
include("ScriptableMC-Tools-TS")
\ No newline at end of file