From 37f11a92f016143dcd202e6be97d73a708145050 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 24 May 2020 14:09:25 -0400 Subject: [PATCH] Add Documentation --- Jenkinsfile | 2 +- README.md | 2 +- build.gradle | 6 +- docs/BRIDGES.md | 68 +++++++++++++++ docs/ENTRY_POINTS.md | 24 +++++ docs/MODULE_RESOLUTION.md | 10 +++ docs/README.md | 8 ++ examples/typescript/README.md | 15 +++- examples/typescript/src/main/ts/tsconfig.json | 2 +- examples/typescript/typescript.build.gradle | 25 +++--- .../scriptcraft/api/ScriptCraftAPI.java | 11 +-- .../scriptcraft/core/ScriptCraftCore.java | 17 ++-- ...ypoint.java => ScriptCraftEntryPoint.java} | 6 +- .../core/quickjs/QuickJSModules.java | 87 ++++++++----------- src/main/ts/package.json | 2 - typescript.build.gradle | 16 ++-- 16 files changed, 191 insertions(+), 110 deletions(-) create mode 100644 docs/BRIDGES.md create mode 100644 docs/ENTRY_POINTS.md create mode 100644 docs/MODULE_RESOLUTION.md create mode 100644 docs/README.md rename src/main/java/com/thebrokenrail/scriptcraft/core/{ScriptCraftEntrypoint.java => ScriptCraftEntryPoint.java} (50%) diff --git a/Jenkinsfile b/Jenkinsfile index 5f97bb2..37bcf33 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,7 +20,7 @@ pipeline { allowMissing: false, alwaysLinkToLastBuild: false, keepAll: false, - reportDir: 'src/main/ts/lib/typedoc', + reportDir: 'src/main/ts/build/typedoc', reportFiles: 'index.html', reportName: 'TypeDoc' ] diff --git a/README.md b/README.md index 8dbea3a..de676c7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ JS API for Minecraft ## API -[View TypeDoc](https://jenkins.thebrokenrail.com/job/ScriptCraft/job/master/TypeDoc/) +[View API](docs/README.md) ## Example [View Examples](examples/README.md) diff --git a/build.gradle b/build.gradle index bcbaa33..513b120 100644 --- a/build.gradle +++ b/build.gradle @@ -69,9 +69,8 @@ publishing { artifact(remapJar) { builtBy remapJar } - artifact(typescriptAPI) { - classifier 'api' - builtBy typescriptAPI + artifact(apiJar) { + builtBy apiJar } } } @@ -79,6 +78,5 @@ publishing { maven { url '/data/maven' } - mavenLocal() } } \ No newline at end of file diff --git a/docs/BRIDGES.md b/docs/BRIDGES.md new file mode 100644 index 0000000..a1e382b --- /dev/null +++ b/docs/BRIDGES.md @@ -0,0 +1,68 @@ +# Bridges +To communicate between Java and JS code, ScriptCraft utilizes a system called bridges. + +## JS-To-Java Bridges + +### Define The Bridge +```java +import com.thebrokenrail.scriptcraft.core.ScriptCraftCore; +import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint; + +public class ExampleBridges implements ScriptCraftEntryPoint { + @Override + public String getEntryPoint() { + return "example-bridges"; + } + + @Override + public void registerBridges() { + ScriptCraftCore.addBridge("ExampleBridges.helloWorld", args -> { + System.out.println("Hello World! " + args[0]); + return "Return Value"; + }); + } +} +``` + +### Use The Bridge +```javascript +import { useBridge } from 'scriptcraft-core'; + +console.log(useBridge('ExampleBridges.helloWorld', 'Argument')); +``` + +## Java-To-JS Bridges + +### Define The Bridge +```javascript +import { addBridge } from 'scriptcraft-core'; + +addBridge('ExampleBridges.helloWorld', arg => { + console.log('Hello World! ' + args[0]); + return 'Return Value'; +}); +``` + +### Use The Bridge +```java +import com.thebrokenrail.scriptcraft.core.ScriptCraftCore; + +public class ExampleBridges { + public static void run() { + System.out.println((String) ScriptCraftCore.useBridge("ExampleBridges.helloWorld", "Argument")); + } +} +``` + +## Supported Types +| JS Type | Java Type | +| --- | --- | +| Number | ```Double``` (object-form) | +| Boolean | ```Boolean``` (object-form) | +| String | ```String``` | +| Array | ```Object[]``` | +| Object | ```null``` | +| ```JavaObject``` | ```Object``` | + +### ```JavaObject``` +A ```JavaObject``` is a normal JS object with an internal reference to a Java object, the Java object reference cannot be accessed directly from JS, but when it is passed back to Java using a bridge, it will become the referenced Java object. \ No newline at end of file diff --git a/docs/ENTRY_POINTS.md b/docs/ENTRY_POINTS.md new file mode 100644 index 0000000..17f18cf --- /dev/null +++ b/docs/ENTRY_POINTS.md @@ -0,0 +1,24 @@ +# Entry-Points +To add an entry-point to your mod create a class implementing ```ScriptCraftEntryPoint``` and override the method ```getEntryPoint()``` to return your entry-point. Then, add your new class to the ```scriptcraft``` entry-point in ```fabric.mod.json```. + +## Example +```java +import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint; + +public class ExampleMod implements ScriptCraftEntryPoint { + @Override + public String getEntryPoint() { + return "example-mod"; + } +} +``` +```json +{ + "entrypoints": { + "scriptcraft": [ + ".ExampleMod" + ] + } +} +``` +This will run ```import `example-mod`;``` on initialization. \ No newline at end of file diff --git a/docs/MODULE_RESOLUTION.md b/docs/MODULE_RESOLUTION.md new file mode 100644 index 0000000..2741a87 --- /dev/null +++ b/docs/MODULE_RESOLUTION.md @@ -0,0 +1,10 @@ +# Module Resolution +All ScriptCraft mods have a folder called ```scriptcraft``` in the root of their JAR file, when ScriptCraft is loaded all of these are combined to form a single "virtual" root. It is recommended to use namespaces to avoid conflicts with other mods. + +## Supported File Types +- ```.js``` +- ```.json``` + +## Extra Details +- If the import path is a folder, ScriptCraft will load the ```index``` file with one of the supported file types. +- If an extension does not exist in the import path it will look for a file with the same name and one of the supported extensions. \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..5af6140 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +# API +[View Module Resolution](MODULE_RESOLUTION.md) + +[View Entry-Points](ENTRY_POINTS.md) + +[View Bridges](BRIDGES.md) + +[View TypeDoc](https://jenkins.thebrokenrail.com/job/ScriptCraft/job/master/TypeDoc/) \ No newline at end of file diff --git a/examples/typescript/README.md b/examples/typescript/README.md index 8744962..8acff95 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -6,6 +6,17 @@ This is an example of a Minecraft mod made in TypeScript using ScriptCraft. - ```src/main/java```: Contains Bootstrap Java Code #### Notes -- When updating the Minecraft version, make sure to update it in the ```src/main/ts/package.json``` file as well - This will also work with JavaScript if you set ```compilerOptions.allowJs``` and optionally ```compilerOptions.checkJs``` in ```src/main/ts/tsconfig.json``` to ```true``` -- NPM dependencies are not bundled \ No newline at end of file +- NPM dependencies are not bundled +- API JARs are not bundled + +#### ```typescript``` Gradle Configuration +The ```typescript``` gradle configuration will extract the specified API JAR into ```src/main/ts/build/dependencies```. + +#### API JAR Format +```$xslt +- src/ + - DTS, TS, JS, or JSON files and folders +- types/ + - File structure of node_modules/@types +``` \ No newline at end of file diff --git a/examples/typescript/src/main/ts/tsconfig.json b/examples/typescript/src/main/ts/tsconfig.json index aee35ec..673bfd0 100644 --- a/examples/typescript/src/main/ts/tsconfig.json +++ b/examples/typescript/src/main/ts/tsconfig.json @@ -10,7 +10,7 @@ "removeComments": true, "typeRoots": ["build/dependencies/types"], "paths": { - "*": ["*", "../build/dependencies/lib/*"] + "*": ["*", "../build/dependencies/src/*"] }, "baseUrl": "src", "outDir": "build/ts", diff --git a/examples/typescript/typescript.build.gradle b/examples/typescript/typescript.build.gradle index 2edc049..138ef8e 100644 --- a/examples/typescript/typescript.build.gradle +++ b/examples/typescript/typescript.build.gradle @@ -5,7 +5,7 @@ def typescriptOut = new File(typescriptRootFile, 'build/ts') def nodeModules = new File(typescriptRootFile, 'node_modules') -task typescriptInstall(group: 'typescript', type: Exec) { +task npmInstall(group: 'typescript', type: Exec) { inputs.file new File(typescriptRootFile, 'package.json') outputs.dir nodeModules @@ -30,21 +30,18 @@ configurations { } } -task extractTypescriptDependencies(group: 'typescript') { - doFirst { - project.delete { - delete typescriptDependencies - } - typescriptDependencies.mkdirs() - configurations.typescript.resolve().each { file -> - project.copy { - from(zipTree(file).matching { - exclude 'META-INF', 'META-INF/**' - }) - into typescriptDependencies +task extractTypescriptDependencies(type: Sync, group: 'typescript') { + dependsOn configurations.typescript + + from { + configurations.typescript.collect { + zipTree(it).matching { + exclude 'META-INF', 'META-INF/**' } } } + + into typescriptDependencies } task typescript(group: 'typescript', type: Exec) { @@ -64,7 +61,7 @@ task typescript(group: 'typescript', type: Exec) { } } -typescript.dependsOn typescriptInstall +typescript.dependsOn npmInstall typescript.dependsOn extractTypescriptDependencies processResources.dependsOn typescript diff --git a/src/main/java/com/thebrokenrail/scriptcraft/api/ScriptCraftAPI.java b/src/main/java/com/thebrokenrail/scriptcraft/api/ScriptCraftAPI.java index 35d02de..ba96360 100644 --- a/src/main/java/com/thebrokenrail/scriptcraft/api/ScriptCraftAPI.java +++ b/src/main/java/com/thebrokenrail/scriptcraft/api/ScriptCraftAPI.java @@ -1,20 +1,15 @@ package com.thebrokenrail.scriptcraft.api; import com.thebrokenrail.scriptcraft.api.bridge.Bridges; -import com.thebrokenrail.scriptcraft.core.ScriptCraftEntrypoint; +import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint; @SuppressWarnings("unused") -public class ScriptCraftAPI implements ScriptCraftEntrypoint { +public class ScriptCraftAPI implements ScriptCraftEntryPoint { @Override - public String getModID() { + public String getEntryPoint() { return "minecraft"; } - @Override - public String getModIndex() { - return "index"; - } - @Override public void registerBridges() { Bridges.init(); diff --git a/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftCore.java b/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftCore.java index f058785..65efbc0 100644 --- a/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftCore.java +++ b/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftCore.java @@ -6,7 +6,6 @@ import com.thebrokenrail.scriptcraft.core.quickjs.QuickJSManager; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.regex.Pattern; @@ -51,18 +50,12 @@ public class ScriptCraftCore implements ModInitializer { QuickJSManager.init(new QuickJSManager.Task() { @Override protected Object run(QuickJSNative quickjs) { - List mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntrypoint.class); - List initializedMods = new ArrayList<>(); - for (ScriptCraftEntrypoint mod : mods) { - if (MOD_ID_PATTERN.matcher(mod.getModID()).matches()) { - mod.registerBridges(); - initializedMods.add(mod); - } else { - System.err.println("Invalid Mod ID: " + mod.getModID()); - } + List mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntryPoint.class); + for (ScriptCraftEntryPoint mod : mods) { + mod.registerBridges(); } - for (ScriptCraftEntrypoint mod : initializedMods) { - quickjs.run("import '" + mod.getModID() + "';"); + for (ScriptCraftEntryPoint mod : mods) { + quickjs.run("import `" + mod.getEntryPoint().replaceAll("`", "\\`") + "`;"); } return null; } diff --git a/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntrypoint.java b/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntryPoint.java similarity index 50% rename from src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntrypoint.java rename to src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntryPoint.java index 9b75e31..f29068b 100644 --- a/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntrypoint.java +++ b/src/main/java/com/thebrokenrail/scriptcraft/core/ScriptCraftEntryPoint.java @@ -1,9 +1,7 @@ package com.thebrokenrail.scriptcraft.core; -public interface ScriptCraftEntrypoint { - String getModID(); - - String getModIndex(); +public interface ScriptCraftEntryPoint { + String getEntryPoint(); default void registerBridges() { } diff --git a/src/main/java/com/thebrokenrail/scriptcraft/core/quickjs/QuickJSModules.java b/src/main/java/com/thebrokenrail/scriptcraft/core/quickjs/QuickJSModules.java index 9c77cd4..c870695 100644 --- a/src/main/java/com/thebrokenrail/scriptcraft/core/quickjs/QuickJSModules.java +++ b/src/main/java/com/thebrokenrail/scriptcraft/core/quickjs/QuickJSModules.java @@ -1,7 +1,7 @@ package com.thebrokenrail.scriptcraft.core.quickjs; import com.thebrokenrail.scriptcraft.core.ScriptCraftCore; -import com.thebrokenrail.scriptcraft.core.ScriptCraftEntrypoint; +import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint; import net.fabricmc.loader.api.FabricLoader; import java.io.BufferedReader; @@ -18,40 +18,40 @@ import java.util.List; @SuppressWarnings("unused") public class QuickJSModules { public static String loadModule(String name) { - String[] arr = name.split(File.separator, 2); - if (ScriptCraftCore.MOD_ID_PATTERN.matcher(arr[0]).matches()) { - List mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntrypoint.class); - if (arr.length == 1) { - return null; - } else { - for (ScriptCraftEntrypoint mod : mods) { - if (mod.getModID().equals(arr[0])) { - //noinspection CatchMayIgnoreException - try { - InputStream stream = mod.getClass().getResourceAsStream(File.separator + ScriptCraftCore.NAMESPACE + File.separator + arr[0] + File.separator + arr[1]); + List mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntryPoint.class); + for (ScriptCraftEntryPoint mod : mods) { + //noinspection CatchMayIgnoreException + try { + InputStream stream = mod.getClass().getResourceAsStream(File.separator + ScriptCraftCore.NAMESPACE + File.separator + name); - if (stream != null) { - StringBuilder textBuilder = new StringBuilder(); - try (Reader reader = new BufferedReader(new InputStreamReader(stream))) { - int c; - while ((c = reader.read()) != -1) { - textBuilder.append((char) c); - } - } - stream.close(); - - return textBuilder.toString(); - } - } catch (IOException e) { + if (stream != null) { + StringBuilder textBuilder = new StringBuilder(); + try (Reader reader = new BufferedReader(new InputStreamReader(stream))) { + int c; + while ((c = reader.read()) != -1) { + textBuilder.append((char) c); } } + stream.close(); + + return textBuilder.toString(); } + } catch (IOException e) { } } return null; } - private static final String[] SUPPORTED_EXTENSION = new String[]{"", ".js", ".json", ".mjs"}; + private static final String[] SUPPORTED_EXTENSIONS; + static { + final String[] supportedExtensions = new String[]{"", ".js", ".json", ".mjs"}; + SUPPORTED_EXTENSIONS = new String[supportedExtensions.length * 2]; + for (int i = 0; i < supportedExtensions.length; i++) { + SUPPORTED_EXTENSIONS[i + supportedExtensions.length] = File.separator + "index" + supportedExtensions[i]; + SUPPORTED_EXTENSIONS[i] = supportedExtensions[i]; + } + } + private static final String[] BUILTIN_MODULES = new String[]{"scriptcraft-core"}; public static String normalizeModule(String baseName, String name) { @@ -61,12 +61,15 @@ public class QuickJSModules { String normalizedPath; + baseName = baseName.replaceAll("/", File.separator); + name = name.replaceAll("/", File.separator); + if (name.startsWith(".")) { - Path parentPath = Paths.get(baseName.replaceAll("/", File.separator)).getParent(); + Path parentPath = Paths.get(baseName).getParent(); if (parentPath == null) { parentPath = Paths.get(File.separator); } - normalizedPath = Paths.get(parentPath.toString(), name.replaceAll("/", File.separator)).normalize().toString(); + normalizedPath = Paths.get(parentPath.toString(), name).normalize().toString(); if (normalizedPath.charAt(0) == File.separatorChar) { normalizedPath = normalizedPath.substring(1); } @@ -74,31 +77,9 @@ public class QuickJSModules { normalizedPath = name; } - String result = null; - boolean success = false; - - String[] arr = normalizedPath.split(File.separator, 2); - if (ScriptCraftCore.MOD_ID_PATTERN.matcher(arr[0]).matches()) { - List mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntrypoint.class); - if (arr.length == 1) { - for (ScriptCraftEntrypoint mod : mods) { - if (mod.getModID().equals(arr[0])) { - result = arr[0] + File.separator + mod.getModIndex(); - success = true; - break; - } - } - } else { - result = arr[0] + File.separator + arr[1]; - success = true; - } - } - - if (success) { - for (String extension : SUPPORTED_EXTENSION) { - if (loadModule(result + extension) != null) { - return result + extension; - } + for (String extension : SUPPORTED_EXTENSIONS) { + if (loadModule(normalizedPath + extension) != null) { + return normalizedPath + extension; } } diff --git a/src/main/ts/package.json b/src/main/ts/package.json index fa2df29..d901d95 100644 --- a/src/main/ts/package.json +++ b/src/main/ts/package.json @@ -1,6 +1,4 @@ { - "name": "scriptcraft-api", - "version": "1.0.0-SNAPSHOT", "devDependencies": { "@typescript-eslint/eslint-plugin": "latest", "@typescript-eslint/parser": "latest", diff --git a/typescript.build.gradle b/typescript.build.gradle index af23f11..1bdef03 100644 --- a/typescript.build.gradle +++ b/typescript.build.gradle @@ -3,7 +3,7 @@ def typescriptRootFile = new File(rootDir.absolutePath, typescriptRoot) def nodeModules = new File(typescriptRootFile, 'node_modules') -task typescriptInstall(group: 'typescript', type: Exec) { +task npmInstall(group: 'typescript', type: Exec) { inputs.file new File(typescriptRootFile, 'package.json') outputs.dir nodeModules @@ -34,23 +34,23 @@ task typescript(group: 'typescript', type: Exec) { } } -typescript.dependsOn typescriptInstall +typescript.dependsOn npmInstall -task typescriptAPI(group: 'typescript', type: Jar) { +task apiJar(group: 'typescript', type: Jar) { into('/types') { from new File(typescriptRootFile as File, 'types') } - into('/lib') { + into('/src') { from dtsOut } classifier 'api' } -typescriptAPI.dependsOn typescript +apiJar.dependsOn typescript artifacts { - archives typescriptAPI + archives apiJar } task eslint(group: 'typescript', type: Exec) { @@ -61,7 +61,7 @@ task eslint(group: 'typescript', type: Exec) { args 'run', 'eslint' } -eslint.dependsOn typescriptInstall +eslint.dependsOn npmInstall processResources.dependsOn typescript @@ -84,7 +84,7 @@ task typedoc(group: 'typescript', type: Exec) { } } -typedoc.dependsOn typescriptInstall +typedoc.dependsOn npmInstall processResources { from(typescriptOut.absolutePath) {