COMPARATOR = Comparator.nullsFirst(Comparator
+ .comparing(SnapshotVersion::getSnapshotDate)
+ .thenComparing(SnapshotVersion::getSnapshotWeekVersion)
+ );
+
+ private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(\\d{2}w\\d{2})([a-z])");
+
+ /**
+ * Parses a snapshot version
+ *
+ * @param version the version string
+ * @return the parsed version
+ * @throws IllegalArgumentException if the version is not a snapshot version
+ */
+ public static SnapshotVersion parse(String version) throws IllegalArgumentException {
+ return new SnapshotVersion(version);
+ }
+
+ private final Date snapshotDate;
+ private final int snapshotWeekVersion;
+
+ private transient String rawString;
+
+ private SnapshotVersion(String version) {
+ Matcher matcher = SNAPSHOT_PATTERN.matcher(version.trim());
+
+ if (matcher.matches()) {
+ try {
+ this.snapshotDate = getDateFormat().parse(matcher.group(1));
+ this.snapshotWeekVersion = matcher.group(2).charAt(0) - 'a';
+ this.rawString = version;
+ } catch (ParseException e) {
+ throw new IllegalArgumentException("Date implied by snapshot version is invalid.", e);
+ }
+ } else {
+ throw new IllegalArgumentException("Cannot parse " + version + " as a snapshot version.");
+ }
+ }
+
+ /**
+ * Retrieve the snapshot date parser.
+ *
+ * We have to create a new instance of SimpleDateFormat every time as it is not thread safe.
+ * @return The date formatter.
+ */
+ private static SimpleDateFormat getDateFormat() {
+ SimpleDateFormat format = new SimpleDateFormat("yy'w'ww", Locale.US);
+ format.setLenient(false);
+ return format;
+ }
+
+ /**
+ * Retrieve the snapshot version within a week, starting at zero.
+ *
+ * @return The weekly version
+ */
+ public int getSnapshotWeekVersion() {
+ return this.snapshotWeekVersion;
+ }
+
+ /**
+ * Retrieve the week this snapshot was released.
+ *
+ * @return The week.
+ */
+ public Date getSnapshotDate() {
+ return this.snapshotDate;
+ }
+
+ /**
+ * Retrieve the raw snapshot string (yy'w'ww[a-z]).
+ *
+ * @return The snapshot string.
+ */
+ public String getSnapshotString() {
+ if (this.rawString == null) {
+ // It's essential that we use the same locale
+ Calendar current = Calendar.getInstance(Locale.US);
+ current.setTime(this.snapshotDate);
+ this.rawString = String.format("%02dw%02d%s",
+ current.get(Calendar.YEAR) % 100,
+ current.get(Calendar.WEEK_OF_YEAR),
+ (char) ('a' + this.snapshotWeekVersion));
+ }
+ return this.rawString;
+ }
+
+ @Override
+ public int compareTo(@NotNull SnapshotVersion that) {
+ return COMPARATOR.compare(this, that);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (obj == this) return true;
+ if (!(obj instanceof SnapshotVersion)) return false;
+
+
+ SnapshotVersion other = (SnapshotVersion) obj;
+ return Objects.equals(this.snapshotDate, other.getSnapshotDate()) &&
+ this.snapshotWeekVersion == other.getSnapshotWeekVersion();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.snapshotDate, this.snapshotWeekVersion);
+ }
+
+ @Override
+ public String toString() {
+ return getSnapshotString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt b/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
index 6e8df034..5df465bd 100644
--- a/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
+++ b/src/main/kotlin/com/pixlfox/scriptablemc/TypescriptLibraryExporter.kt
@@ -13,7 +13,7 @@ import java.lang.reflect.*
class TypescriptLibraryExporter {
private var basePath: String = "./lib"
private val classList = mutableListOf>()
- private var allowedPackagesRegex: Regex = Regex("(org\\.bukkit|com\\.pixlfox|fr\\.minuskube\\.inv|com\\.google|java\\.sql)(.*)?")
+ private var allowedPackagesRegex: Regex = Regex("(org\\.bukkit|com\\.pixlfox|com\\.smc|fr\\.minuskube\\.inv|com\\.google|java\\.sql|java\\.io)(.*)?")
private val paranamer: Paranamer = BytecodeReadingParanamer()
private fun safeName(name: String): String = when {
@@ -86,11 +86,20 @@ class TypescriptLibraryExporter {
addClasses(
com.pixlfox.scriptablemc.core.ScriptablePluginContext::class.java,
com.pixlfox.scriptablemc.core.ScriptablePluginEngine::class.java,
+
+ com.smc.utils.ItemBuilder::class.java,
+ com.smc.utils.MysqlWrapper::class.java,
+
+ com.smc.version.MinecraftVersion::class.java,
+ com.smc.version.MinecraftVersions::class.java,
+
fr.minuskube.inv.SmartInventory::class.java,
- com.pixlfox.scriptablemc.utils.FileWrapper::class.java,
- com.pixlfox.scriptablemc.smartinvs.SmartInventoryProvider::class.java,
- com.pixlfox.scriptablemc.smartinvs.SmartInventoryInterface::class.java,
- com.google.common.io.ByteStreams::class.java
+ com.smc.smartinvs.SmartInventoryProvider::class.java,
+ com.smc.smartinvs.SmartInventory::class.java,
+
+ com.google.common.io.ByteStreams::class.java,
+
+ java.io.File::class.java
)
return this
}
@@ -232,6 +241,14 @@ class TypescriptLibraryExporter {
}
}
+ val blacklistRegex = Regex("(spigot|wait|equals|toString|hashCode|getClass|notify|notifyAll|Companion)")
+ for (_field in _class.fields.filter { Modifier.isStatic(it.modifiers) &&Modifier.isPublic(it.modifiers) && !it.name.matches(blacklistRegex) }) {
+ val type = fixClass(_field.type)
+ if (!classList.contains(type) && type.name.matches(allowedPackagesRegex)) {
+ classList.add(type)
+ }
+ }
+
return classList.toTypedArray()
}
@@ -316,6 +333,12 @@ class TypescriptLibraryExporter {
return this
}
+ fun copyStaticSources(): TypescriptLibraryExporter {
+ File("./src/main/ts/").copyRecursively(File("$basePath/ts/"), true)
+
+ return this
+ }
+
fun exportProjectFiles(): TypescriptLibraryExporter {
File("$basePath/tsconfig.json").writeText("{\n" +
" \"compilerOptions\": {\n" +
@@ -325,8 +348,7 @@ class TypescriptLibraryExporter {
" \"allowJs\": true,\n" +
" \"outDir\": \"js\",\n" +
" \"rootDir\": \"ts\",\n" +
- " \"declaration\": true,\n" +
- " \"lib\": [\"ES5\", \"ES2015\", \"ES2016\", \"ES2017\", \"ES2018\", \"ES2019\"]\n" +
+ " \"declaration\": true\n" +
" },\n" +
" \"include\": [\n" +
" \"ts/**/*\"\n" +
@@ -394,7 +416,7 @@ class TypescriptLibraryExporter {
for (_class in classList) {
if(_class.name.matches(allowedPackagesRegex) && !_class.name.endsWith("\$Spigot")) {
- tsGlobalExportsSource += generateTypescriptImportForClass(_class);
+ tsGlobalExportsSource += generateTypescriptImportForClass(_class)
}
}
@@ -475,7 +497,7 @@ class TypescriptLibraryExporter {
}
}
- for (_methodGroups in _class.methods.filter { e -> Modifier.isStatic(e.modifiers) && Modifier.isPublic(e.modifiers) }.groupBy { e -> e.name }) {
+ for (_methodGroups in _class.methods.filter { e -> Modifier.isStatic(e.modifiers) && !Modifier.isPrivate(e.modifiers) }.groupBy { e -> e.name }) {
val jsMethodName: String = _methodGroups.key
@@ -628,6 +650,7 @@ class TypescriptLibraryExporter {
.clean()
.exportLibraries()
.exportGlobalLibrary()
+ .copyStaticSources()
.exportProjectFiles()
}
}
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt b/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
index b86ba53e..12504fd9 100644
--- a/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
+++ b/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginContext.kt
@@ -1,9 +1,5 @@
package com.pixlfox.scriptablemc.core
-import com.pixlfox.scriptablemc.smartinvs.SmartInventoryInterface
-import com.pixlfox.scriptablemc.smartinvs.SmartItemBuilder
-import com.pixlfox.scriptablemc.utils.FileWrapper
-import com.pixlfox.scriptablemc.utils.MysqlWrapper
import me.clip.placeholderapi.PlaceholderAPI
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
@@ -17,7 +13,6 @@ import java.util.HashMap
import java.lang.reflect.InvocationTargetException
import org.bukkit.command.PluginCommand
import org.bukkit.entity.Player
-import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.*
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.plugin.messaging.PluginMessageListener
@@ -128,7 +123,12 @@ class ScriptablePluginContext(private val engine: ScriptablePluginEngine, val pl
commandMapField.isAccessible = true
val commandMap = commandMapField.get(Bukkit.getServer()) as CommandMap
- val knownCommandsField = commandMap.javaClass.superclass.declaredFields.firstOrNull { it.name.equals("knownCommands", false) }
+ var knownCommandsField = commandMap.javaClass.superclass.declaredFields.firstOrNull { it.name.equals("knownCommands", false) }
+
+ if(knownCommandsField == null) { // Pre-MCv1.13 command unregister fix
+ knownCommandsField = commandMap.javaClass.declaredFields.firstOrNull { it.name.equals("knownCommands", false) }
+ }
+
knownCommandsField?.isAccessible = true
val knownCommands = knownCommandsField?.get(commandMap) as HashMap<*, *>?
@@ -141,22 +141,6 @@ class ScriptablePluginContext(private val engine: ScriptablePluginEngine, val pl
knownCommandsField?.isAccessible = false
}
- fun getFile(pathName: String): FileWrapper {
- return FileWrapper(pathName)
- }
-
- fun newMysqlInstance(host: String, port: Int, database: String, username: String, password: String): MysqlWrapper {
- return MysqlWrapper(host, port, database, username, password)
- }
-
- fun smartInventory(): SmartInventoryInterface {
- return SmartInventoryInterface()
- }
-
- fun itemBuilder(itemStack: ItemStack): SmartItemBuilder {
- return SmartItemBuilder(itemStack)
- }
-
fun setPlaceholders(player: Player, placeholderText: String): String {
if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
return PlaceholderAPI.setPlaceholders(player, placeholderText)
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt b/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
index 4a1bcfde..468b74b4 100644
--- a/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
+++ b/src/main/kotlin/com/pixlfox/scriptablemc/core/ScriptablePluginEngine.kt
@@ -10,6 +10,20 @@ import java.io.File
import org.bukkit.plugin.java.JavaPlugin
import java.util.*
+private val helperClasses: Array = arrayOf(
+ "com.smc.version.MinecraftVersion",
+ "com.smc.version.MinecraftVersions",
+ "com.smc.version.SnapshotVersion",
+
+ "com.smc.utils.ItemBuilder",
+ "com.smc.utils.MysqlWrapper",
+
+ "com.smc.smartinvs.SmartInventory",
+ "com.smc.smartinvs.SmartInventoryProvider",
+
+ "*me.clip.placeholderapi.PlaceholderAPI"
+
+)
@Suppress("MemberVisibilityCanBePrivate", "unused")
class ScriptablePluginEngine(val bootstrapPlugin: JavaPlugin, val rootScriptsFolder: String = "./scripts", val debugEnabled: Boolean = false, val extractLibs: Boolean = true): Listener {
@@ -32,6 +46,18 @@ class ScriptablePluginEngine(val bootstrapPlugin: JavaPlugin, val rootScriptsFol
inventoryManager.init()
jsBindings.putMember("engine", this)
+ for(helperClass in helperClasses) {
+ try {
+ javaClass.classLoader.loadClass(helperClass.replace("*", ""))
+ }
+ catch (e: Exception) {
+ if(!helperClass.startsWith("*")) {
+ bootstrapPlugin.logger.warning("Failed to load helper class \"$helperClass\" via classloader.")
+ e.printStackTrace()
+ }
+ }
+ }
+
val mainScriptFile = File("${rootScriptsFolder}/main.js")
if(!mainScriptFile.parentFile.exists()) {
mainScriptFile.parentFile.mkdirs()
@@ -45,7 +71,7 @@ class ScriptablePluginEngine(val bootstrapPlugin: JavaPlugin, val rootScriptsFol
}
if(mainScriptFile.exists()) {
- val pluginTypes = eval(
+ val mainReturn = eval(
Source.newBuilder("js", mainScriptFile)
.name("main.js")
.mimeType("application/javascript+module")
@@ -54,15 +80,15 @@ class ScriptablePluginEngine(val bootstrapPlugin: JavaPlugin, val rootScriptsFol
)
// Load all plugin types returned as an array
- if(pluginTypes.hasArrayElements()) {
- for (i in 0..pluginTypes.arraySize) {
- this.loadPlugin(pluginTypes.getArrayElement(i))
+ 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()
+ // Enable all plugins if not already enabled
+ if(!enabledAllPlugins) {
+ enableAllPlugins()
+ }
}
}
else {
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/ScriptablePluginMenu.kt b/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/ScriptablePluginMenu.kt
index 4c85aca8..394aaf95 100644
--- a/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/ScriptablePluginMenu.kt
+++ b/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/ScriptablePluginMenu.kt
@@ -1,6 +1,7 @@
package com.pixlfox.scriptablemc.smartinvs
import com.pixlfox.scriptablemc.core.ScriptablePluginEngine
+import com.smc.utils.ItemBuilder
import fr.minuskube.inv.content.InventoryContents
import fr.minuskube.inv.content.InventoryProvider
import org.bukkit.entity.Player
@@ -18,34 +19,26 @@ import fr.minuskube.inv.content.SlotIterator
class MainMenu(private val pluginEngine: ScriptablePluginEngine) : InventoryProvider {
override fun init(player: Player?, contents: InventoryContents?) {
if(contents != null && player != null) {
- contents.fillBorders(ClickableItem.empty(ItemStack(Material.BLACK_CONCRETE)))
-
- if(player.hasPermission("scriptablemc.js.reload")) {
- val reloadItemStack = ItemStack(Material.GOLD_NUGGET)
- val reloadItemMeta = reloadItemStack.itemMeta
- reloadItemMeta?.setDisplayName("${ChatColor.DARK_AQUA}Reload Script Engine")
- reloadItemStack.itemMeta = reloadItemMeta
- contents.set(1, 1, ClickableItem.of(reloadItemStack) {
- Bukkit.getServer().dispatchCommand(player, "jsreload")
+ if(player.hasPermission("scriptablemc.reload")) {
+ contents.set(0, 0, ClickableItem.of(ItemBuilder(Material.GOLD_NUGGET)
+ .setDisplayName("${ChatColor.DARK_AQUA}Reload Script Engine")
+ .build()) {
+ Bukkit.getServer().dispatchCommand(player, "scriptablemc reload")
})
}
if(player.hasPermission("scriptablemc.info")) {
- val pluginsItemStack = ItemStack(Material.IRON_NUGGET)
- val pluginsItemMeta = pluginsItemStack.itemMeta
- pluginsItemMeta?.setDisplayName("${ChatColor.GREEN}Print ScriptableMC Info")
- pluginsItemStack.itemMeta = pluginsItemMeta
- contents.set(1, 2, ClickableItem.of(pluginsItemStack) {
+ contents.set(0, 1, ClickableItem.of(ItemBuilder(Material.IRON_NUGGET)
+ .setDisplayName("${ChatColor.GREEN}Print ScriptableMC Info")
+ .build()) {
Bukkit.getServer().dispatchCommand(player, "scriptablemc info")
player.closeInventory()
})
}
- val closeItemStack = ItemStack(Material.BARRIER)
- val closeItemMeta = closeItemStack.itemMeta
- closeItemMeta?.setDisplayName("${ChatColor.RED}Close Menu")
- closeItemStack.itemMeta = closeItemMeta
- contents.set(1, 7, ClickableItem.of(closeItemStack) {
+ contents.set(0, 8, ClickableItem.of(ItemBuilder(Material.BARRIER)
+ .setDisplayName("${ChatColor.RED}Close Menu")
+ .build()) {
player.closeInventory()
})
}
@@ -59,7 +52,7 @@ class MainMenu(private val pluginEngine: ScriptablePluginEngine) : InventoryProv
.id("spm.mainmenu")
.manager(ScriptablePluginEngine.instance!!.inventoryManager)
.provider(MainMenu(ScriptablePluginEngine.instance!!))
- .size(3, 9)
+ .size(1, 9)
.title(ChatColor.LIGHT_PURPLE.toString() + "ScriptableMC | Main Menu")
.closeable(true)
.build()
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/SmartInventoryInterface.kt b/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/SmartInventoryInterface.kt
deleted file mode 100644
index 751e2f49..00000000
--- a/src/main/kotlin/com/pixlfox/scriptablemc/smartinvs/SmartInventoryInterface.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.pixlfox.scriptablemc.smartinvs
-
-import com.pixlfox.scriptablemc.core.ScriptablePluginEngine
-import fr.minuskube.inv.ClickableItem
-import fr.minuskube.inv.SmartInventory
-import fr.minuskube.inv.content.InventoryContents
-import fr.minuskube.inv.content.InventoryProvider
-import org.bukkit.enchantments.Enchantment
-import org.bukkit.entity.Player
-import org.bukkit.event.inventory.InventoryClickEvent
-import org.bukkit.inventory.ItemStack
-import org.graalvm.polyglot.Value
-import java.util.function.Consumer
-
-@Suppress("unused", "MemberVisibilityCanBePrivate")
-class SmartInventoryInterface {
- fun builder(): SmartInventory.Builder {
- return SmartInventory.builder().manager(ScriptablePluginEngine.instance!!.inventoryManager)
- }
-
- fun provider(scriptableObject: Value): SmartInventoryProvider {
- if(scriptableObject.canInstantiate()) {
- return provider(scriptableObject.newInstance())
- }
-
- return SmartInventoryProvider(scriptableObject)
- }
-
- fun clickableItem(item: ItemStack): ClickableItem {
- return ClickableItem.empty(item)
- }
-
- fun clickableItem(item: ItemStack, consumer: Consumer): ClickableItem {
- return ClickableItem.of(item, consumer)
- }
-
- fun itemBuilder(itemStack: ItemStack): SmartItemBuilder {
- return SmartItemBuilder(itemStack)
- }
-}
-
-class SmartItemBuilder(private val itemStack: ItemStack) {
- fun setDisplayName(displayName: String): SmartItemBuilder {
- val itemMeta = itemStack.itemMeta
- itemMeta?.setDisplayName(displayName)
- itemStack.itemMeta = itemMeta
-
- return this
- }
-
- fun getDisplayName(): String {
- val itemMeta = itemStack.itemMeta
- if(itemMeta != null) {
- return itemMeta.displayName
- }
-
- return ""
- }
-
- fun setLore(lore: Array): SmartItemBuilder {
- val itemMeta = itemStack.itemMeta
- itemMeta?.lore = lore.toList()
- itemStack.itemMeta = itemMeta
-
- return this
- }
-
- fun getLore(): Array {
- val itemMeta = itemStack.itemMeta
- if(itemMeta != null && itemMeta.lore != null) {
- return itemMeta.lore!!.toTypedArray()
- }
-
- return arrayOf()
- }
-
- fun isUnbreakable(isUnbreakable: Boolean): SmartItemBuilder {
- val itemMeta = itemStack.itemMeta
- itemMeta?.isUnbreakable = isUnbreakable
- itemStack.itemMeta = itemMeta
-
- return this
- }
-
- fun addEnchant(enchantment: Enchantment, level: Int, ignoreLevelRestriction: Boolean): SmartItemBuilder {
- val itemMeta = itemStack.itemMeta
- itemMeta?.addEnchant(enchantment, level, ignoreLevelRestriction)
- itemStack.itemMeta = itemMeta
-
- return this
- }
-
- fun removeEnchantment(enchantment: Enchantment): SmartItemBuilder {
- val itemMeta = itemStack.itemMeta
- itemMeta?.removeEnchant(enchantment)
- itemStack.itemMeta = itemMeta
-
- return this
- }
-
- fun build(): ItemStack {
- return itemStack
- }
-}
-
-class SmartInventoryProvider(private val scriptableObject: Value) : InventoryProvider {
- override fun init(player: Player, contents: InventoryContents) {
- if(scriptableObject.hasMember("init") && scriptableObject.canInvokeMember("init")) {
- scriptableObject.invokeMember("init", player, contents)
- }
- }
-
- override fun update(player: Player, contents: InventoryContents) {
- if(scriptableObject.hasMember("update") && scriptableObject.canInvokeMember("update")) {
- scriptableObject.invokeMember("update", player, contents)
- }
- }
-}
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/utils/FileWrapper.kt b/src/main/kotlin/com/pixlfox/scriptablemc/utils/FileWrapper.kt
deleted file mode 100644
index 602bb342..00000000
--- a/src/main/kotlin/com/pixlfox/scriptablemc/utils/FileWrapper.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.pixlfox.scriptablemc.utils
-
-
-@Suppress("unused")
-class FileWrapper(pathName: String) {
-
- private val javaFile: java.io.File = java.io.File(pathName)
-
- fun readText(): String = javaFile.readText()
- fun readLines(): Array = javaFile.readLines().toTypedArray()
- fun writeText(text: String): Unit = javaFile.writeText(text)
- fun exists(): Boolean = javaFile.exists()
- fun createNewFile(): Boolean = javaFile.createNewFile()
- fun isFile(): Boolean = javaFile.isFile
- fun mkdir(): Boolean = javaFile.mkdir()
- fun mkdirs(): Boolean = javaFile.mkdirs()
- fun isDirectory(): Boolean = javaFile.isDirectory
- fun parentFile(): FileWrapper = new(javaFile.parentFile)
-
- companion object {
- internal fun new(javaFile: java.io.File): FileWrapper {
- return FileWrapper(javaFile.path)
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/smc/smartinvs/SmartInventory.kt b/src/main/kotlin/com/smc/smartinvs/SmartInventory.kt
new file mode 100644
index 00000000..3e3957be
--- /dev/null
+++ b/src/main/kotlin/com/smc/smartinvs/SmartInventory.kt
@@ -0,0 +1,53 @@
+package com.smc.smartinvs
+
+import com.pixlfox.scriptablemc.core.ScriptablePluginEngine
+import fr.minuskube.inv.ClickableItem
+import fr.minuskube.inv.SmartInventory
+import fr.minuskube.inv.content.InventoryContents
+import fr.minuskube.inv.content.InventoryProvider
+import org.bukkit.entity.Player
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.inventory.ItemStack
+import org.graalvm.polyglot.Value
+import java.util.function.Consumer
+
+@Suppress("unused", "MemberVisibilityCanBePrivate")
+object SmartInventory {
+ @JvmStatic
+ fun builder(): SmartInventory.Builder {
+ return SmartInventory.builder().manager(ScriptablePluginEngine.instance!!.inventoryManager)
+ }
+
+ @JvmStatic
+ fun provider(scriptableObject: Value): SmartInventoryProvider {
+ if (scriptableObject.canInstantiate()) {
+ return provider(scriptableObject.newInstance())
+ }
+
+ return SmartInventoryProvider(scriptableObject)
+ }
+
+ @JvmStatic
+ fun clickableItem(item: ItemStack): ClickableItem {
+ return ClickableItem.empty(item)
+ }
+
+ @JvmStatic
+ fun clickableItem(item: ItemStack, consumer: Consumer): ClickableItem {
+ return ClickableItem.of(item, consumer)
+ }
+}
+
+class SmartInventoryProvider(private val scriptableObject: Value) : InventoryProvider {
+ override fun init(player: Player, contents: InventoryContents) {
+ if(scriptableObject.hasMember("init") && scriptableObject.canInvokeMember("init")) {
+ scriptableObject.invokeMember("init", player, contents)
+ }
+ }
+
+ override fun update(player: Player, contents: InventoryContents) {
+ if(scriptableObject.hasMember("update") && scriptableObject.canInvokeMember("update")) {
+ scriptableObject.invokeMember("update", player, contents)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/smc/utils/ItemBuilder.kt b/src/main/kotlin/com/smc/utils/ItemBuilder.kt
new file mode 100644
index 00000000..1bc8c62d
--- /dev/null
+++ b/src/main/kotlin/com/smc/utils/ItemBuilder.kt
@@ -0,0 +1,73 @@
+package com.smc.utils
+
+import org.bukkit.Material
+import org.bukkit.enchantments.Enchantment
+import org.bukkit.inventory.ItemStack
+
+@Suppress("unused")
+class ItemBuilder(private val itemStack: ItemStack) {
+
+ constructor(material: Material): this(ItemStack(material))
+
+ fun setDisplayName(displayName: String): ItemBuilder {
+ val itemMeta = itemStack.itemMeta
+ itemMeta?.setDisplayName(displayName)
+ itemStack.itemMeta = itemMeta
+
+ return this
+ }
+
+ fun getDisplayName(): String {
+ val itemMeta = itemStack.itemMeta
+ if(itemMeta != null) {
+ return itemMeta.displayName
+ }
+
+ return ""
+ }
+
+ fun setLore(lore: Array): ItemBuilder {
+ val itemMeta = itemStack.itemMeta
+ itemMeta?.lore = lore.toList()
+ itemStack.itemMeta = itemMeta
+
+ return this
+ }
+
+ fun getLore(): Array {
+ val itemMeta = itemStack.itemMeta
+ if(itemMeta != null && itemMeta.lore != null) {
+ return itemMeta.lore!!.toTypedArray()
+ }
+
+ return arrayOf()
+ }
+
+ fun isUnbreakable(isUnbreakable: Boolean): ItemBuilder {
+ val itemMeta = itemStack.itemMeta
+ itemMeta?.isUnbreakable = isUnbreakable
+ itemStack.itemMeta = itemMeta
+
+ return this
+ }
+
+ fun addEnchant(enchantment: Enchantment, level: Int, ignoreLevelRestriction: Boolean): ItemBuilder {
+ val itemMeta = itemStack.itemMeta
+ itemMeta?.addEnchant(enchantment, level, ignoreLevelRestriction)
+ itemStack.itemMeta = itemMeta
+
+ return this
+ }
+
+ fun removeEnchantment(enchantment: Enchantment): ItemBuilder {
+ val itemMeta = itemStack.itemMeta
+ itemMeta?.removeEnchant(enchantment)
+ itemStack.itemMeta = itemMeta
+
+ return this
+ }
+
+ fun build(): ItemStack {
+ return itemStack
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/pixlfox/scriptablemc/utils/MysqlWrapper.kt b/src/main/kotlin/com/smc/utils/MysqlWrapper.kt
similarity index 98%
rename from src/main/kotlin/com/pixlfox/scriptablemc/utils/MysqlWrapper.kt
rename to src/main/kotlin/com/smc/utils/MysqlWrapper.kt
index e002792f..8c91d77b 100644
--- a/src/main/kotlin/com/pixlfox/scriptablemc/utils/MysqlWrapper.kt
+++ b/src/main/kotlin/com/smc/utils/MysqlWrapper.kt
@@ -1,4 +1,4 @@
-package com.pixlfox.scriptablemc.utils
+package com.smc.utils
import com.pixlfox.scriptablemc.ScriptablePluginMain
import org.bukkit.Bukkit
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 53e870dd..e548806e 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
name: ScriptableMC
description: 'Run JavaScript/TypeScript plugins for Bukkit/Spigot/Paper Minecraft 1.15'
-version: '1.1.8'
+version: '1.1.9'
api-version: '1.13'
author: AStorks
main: com.pixlfox.scriptablemc.ScriptablePluginMain
diff --git a/src/main/ts/JsPlugin.ts b/src/main/ts/JsPlugin.ts
new file mode 100644
index 00000000..604108f5
--- /dev/null
+++ b/src/main/ts/JsPlugin.ts
@@ -0,0 +1,75 @@
+import Event from "./org/bukkit/event/Event.js";
+import Server from "./org/bukkit/Server.js";
+import ScriptablePluginContext from "./com/pixlfox/scriptablemc/core/ScriptablePluginContext.js";
+import PluginCommand from "./org/bukkit/command/PluginCommand.js";
+import Player from "./org/bukkit/entity/Player.js";
+import PluginMessageListenerRegistration from "./org/bukkit/plugin/messaging/PluginMessageListenerRegistration.js";
+import OfflinePlayer from "./org/bukkit/OfflinePlayer.js";
+import MysqlWrapper from "./com/smc/utils/MysqlWrapper.js";
+import File from "./java/io/File.js";
+
+declare type Type = { new (...args: any[]): T; };
+
+export default class JsPlugin {
+ public context: ScriptablePluginContext;
+
+ get pluginName(): string {
+ return this.constructor.name;
+ }
+
+ get server(): Server {
+ return this.context.getServer();
+ }
+
+ registerEvent(eventClass: Type, callback: (listener: any, event: T) => void) {
+ this.context.registerEvent(eventClass['$javaClass'], callback.bind(this));
+ }
+
+ newCommand(name: string): PluginCommand {
+ return this.context.newCommand(name);
+ }
+
+ unregisterCommand(command: PluginCommand) {
+ this.context.unregisterCommand(command);
+ }
+
+ registerCommand(command: PluginCommand) {
+ this.context.registerCommand(command);
+ }
+
+ registerIncomingPluginChannel(channel: string, callback: (channel: string, player: Player, message: number[]) => void): PluginMessageListenerRegistration {
+ return this.context.registerIncomingPluginChannel(channel, callback.bind(this));
+ }
+
+ unregisterIncomingPluginChannel(channel: string) {
+ this.context.unregisterIncomingPluginChannel(channel);
+ }
+
+ registerOutgoingPluginChannel(channel: string) {
+ this.context.registerOutgoingPluginChannel(channel);
+ }
+
+ unregisterOutgoingPluginChannel(channel: string) {
+ this.context.unregisterOutgoingPluginChannel(channel);
+ }
+
+ getFile(pathName: string): File {
+ return new File(pathName);
+ }
+
+ newMysqlInstance(host: string, port: number, database: string, username: string, password: string): MysqlWrapper {
+ return new MysqlWrapper(host, port, database, username, password);
+ }
+
+ mysqlFromConfig(configObject: { host: string, port: number, database: string, username: string, password: string }): MysqlWrapper {
+ return this.newMysqlInstance(configObject.host, configObject.port, configObject.database, configObject.username, configObject.password);
+ }
+
+ setPlaceholders(player: (Player | OfflinePlayer), placeholderText: string): string {
+ return this.context.setPlaceholders(player, placeholderText);
+ }
+
+ onLoad(): void { console.log("[" + this.pluginName + "] onLoad()"); }
+ onEnable(): void { console.log("[" + this.pluginName + "] onEnable()"); }
+ onDisable(): void { console.log("[" + this.pluginName + "] onDisable()"); }
+}
\ No newline at end of file