This commit is contained in:
parent
48796de9de
commit
37f11a92f0
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -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'
|
||||
]
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
68
docs/BRIDGES.md
Normal file
68
docs/BRIDGES.md
Normal file
@ -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.
|
24
docs/ENTRY_POINTS.md
Normal file
24
docs/ENTRY_POINTS.md
Normal file
@ -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": [
|
||||
"<Package>.ExampleMod"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
This will run ```import `example-mod`;``` on initialization.
|
10
docs/MODULE_RESOLUTION.md
Normal file
10
docs/MODULE_RESOLUTION.md
Normal file
@ -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.
|
8
docs/README.md
Normal file
8
docs/README.md
Normal file
@ -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/)
|
@ -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
|
||||
- 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
|
||||
```
|
@ -10,7 +10,7 @@
|
||||
"removeComments": true,
|
||||
"typeRoots": ["build/dependencies/types"],
|
||||
"paths": {
|
||||
"*": ["*", "../build/dependencies/lib/*"]
|
||||
"*": ["*", "../build/dependencies/src/*"]
|
||||
},
|
||||
"baseUrl": "src",
|
||||
"outDir": "build/ts",
|
||||
|
@ -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 {
|
||||
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
|
||||
|
@ -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();
|
||||
|
@ -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<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntrypoint.class);
|
||||
List<ScriptCraftEntrypoint> initializedMods = new ArrayList<>();
|
||||
for (ScriptCraftEntrypoint mod : mods) {
|
||||
if (MOD_ID_PATTERN.matcher(mod.getModID()).matches()) {
|
||||
List<ScriptCraftEntryPoint> mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntryPoint.class);
|
||||
for (ScriptCraftEntryPoint mod : mods) {
|
||||
mod.registerBridges();
|
||||
initializedMods.add(mod);
|
||||
} else {
|
||||
System.err.println("Invalid Mod ID: " + mod.getModID());
|
||||
}
|
||||
}
|
||||
for (ScriptCraftEntrypoint mod : initializedMods) {
|
||||
quickjs.run("import '" + mod.getModID() + "';");
|
||||
for (ScriptCraftEntryPoint mod : mods) {
|
||||
quickjs.run("import `" + mod.getEntryPoint().replaceAll("`", "\\`") + "`;");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package com.thebrokenrail.scriptcraft.core;
|
||||
|
||||
public interface ScriptCraftEntrypoint {
|
||||
String getModID();
|
||||
|
||||
String getModIndex();
|
||||
public interface ScriptCraftEntryPoint {
|
||||
String getEntryPoint();
|
||||
|
||||
default void registerBridges() {
|
||||
}
|
@ -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,17 +18,11 @@ 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<ScriptCraftEntrypoint> 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])) {
|
||||
List<ScriptCraftEntryPoint> 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 + arr[0] + File.separator + arr[1]);
|
||||
InputStream stream = mod.getClass().getResourceAsStream(File.separator + ScriptCraftCore.NAMESPACE + File.separator + name);
|
||||
|
||||
if (stream != null) {
|
||||
StringBuilder textBuilder = new StringBuilder();
|
||||
@ -45,13 +39,19 @@ public class QuickJSModules {
|
||||
} 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<ScriptCraftEntrypoint> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
{
|
||||
"name": "scriptcraft-api",
|
||||
"version": "1.0.0-SNAPSHOT",
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "latest",
|
||||
"@typescript-eslint/parser": "latest",
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user