Initial Commit
This commit is contained in:
commit
6b6c4a91b8
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# gradle
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
out/
|
||||
classes/
|
||||
|
||||
# idea
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# vscode
|
||||
|
||||
.settings/
|
||||
.vscode/
|
||||
bin/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# fabric
|
||||
|
||||
run/
|
||||
|
||||
remappedSrc/
|
||||
|
||||
src/main/c/build-*
|
||||
src/main/c/quickjs
|
||||
src/main/c/jni
|
||||
|
||||
scripts/jdk.tar.gz
|
||||
scripts/jdk
|
||||
scripts/quickjs.tar.xz
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Changelog
|
||||
|
||||
**1.0**
|
||||
* Initial Release
|
4
Dockerfile
Normal file
4
Dockerfile
Normal file
@ -0,0 +1,4 @@
|
||||
FROM openjdk:8-jdk-alpine
|
||||
|
||||
RUN apk add --no-cache npm
|
||||
RUN npm install -g typescript typedoc
|
22
Jenkinsfile
vendored
Normal file
22
Jenkinsfile
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
pipeline {
|
||||
agent {
|
||||
dockerfile true
|
||||
}
|
||||
stages {
|
||||
stage('Setup') {
|
||||
steps {
|
||||
sh 'cd scripts; ./setup.sh'
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './gradlew build'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
archiveArtifacts artifacts: 'build/libs/*', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 TheBrokenRail
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# ScriptCraft
|
||||
JS API for Minecraft
|
||||
|
||||
## Changelog
|
||||
[View Changelog](CHANGELOG.md)
|
218
build.gradle
Normal file
218
build.gradle
Normal file
@ -0,0 +1,218 @@
|
||||
plugins {
|
||||
id 'fabric-loom' version '0.2.7-SNAPSHOT'
|
||||
id 'com.matthewprenger.cursegradle' version '1.4.0'
|
||||
}
|
||||
|
||||
def cDir = new File(rootProject.projectDir.absolutePath, 'src/main/c')
|
||||
|
||||
class JNIPlatform {
|
||||
String name
|
||||
String[] cmakeArgs
|
||||
String libExtension
|
||||
|
||||
JNIPlatform(String name, String[] cmakeArgs, String libExtension) {
|
||||
this.name = name
|
||||
this.cmakeArgs = cmakeArgs
|
||||
this.libExtension = libExtension
|
||||
}
|
||||
}
|
||||
|
||||
JNIPlatform[] jniPlatforms = [
|
||||
new JNIPlatform("linux-x86_64", [] as String[], ".so"),
|
||||
new JNIPlatform("linux-i686", ["-DCMAKE_TOOLCHAIN_FILE=${rootDir.absolutePath}/cmake/linux_i686_toolchain.cmake"] as String[], ".so"),
|
||||
new JNIPlatform("windows-x86_64", ["-DCMAKE_TOOLCHAIN_FILE=${rootDir.absolutePath}/cmake/windows_x86_64_toolchain.cmake"] as String[], ".dll"),
|
||||
new JNIPlatform("windows-i686", ["-DCMAKE_TOOLCHAIN_FILE=${rootDir.absolutePath}/cmake/windows_i686_toolchain.cmake"] as String[], ".dll")
|
||||
]
|
||||
|
||||
for (JNIPlatform platform : jniPlatforms) {
|
||||
def buildDir = new File(cDir, "build-${platform.name}")
|
||||
if (!buildDir.exists()) {
|
||||
buildDir.mkdir()
|
||||
}
|
||||
|
||||
tasks.create(name: "cmake-${platform.name}", type: Exec) {
|
||||
workingDir buildDir
|
||||
|
||||
executable 'cmake'
|
||||
args platform.cmakeArgs + ['..']
|
||||
}
|
||||
|
||||
tasks.create(name: "compileJNI-${platform.name}", type: Exec) {
|
||||
workingDir buildDir
|
||||
|
||||
executable 'make'
|
||||
|
||||
dependsOn tasks.getByName("cmake-${platform.name}")
|
||||
}
|
||||
|
||||
processResources.dependsOn tasks.getByName("compileJNI-${platform.name}")
|
||||
|
||||
tasks.create(name: "cleanJNI-${platform.name}", type: Delete) {
|
||||
delete buildDir.absolutePath
|
||||
}
|
||||
|
||||
clean.dependsOn tasks.getByName("cleanJNI-${platform.name}")
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
def mod_version = project.mod_version as Object
|
||||
version = "${mod_version}+${project.minecraft_version}"
|
||||
group = project.maven_group as Object
|
||||
|
||||
minecraft {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings "net.fabricmc:yarn:${project.minecraft_version}+build.${project.yarn_build}:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
|
||||
}
|
||||
|
||||
def typescriptOut = new File(buildDir, 'generated/typescript')
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDir typescriptOut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def typescriptRoot = rootProject.typescript_root as String
|
||||
def resources = new File(rootProject.rootDir, 'src/main/resources')
|
||||
def typescriptRootFile = new File(resources, typescriptRoot)
|
||||
|
||||
task compileTypescript {
|
||||
inputs.dir typescriptRootFile
|
||||
outputs.dir typescriptOut
|
||||
|
||||
doFirst {
|
||||
project.delete {
|
||||
delete typescriptOut
|
||||
}
|
||||
typescriptOut.mkdirs()
|
||||
|
||||
project.exec {
|
||||
executable 'tsc'
|
||||
|
||||
args '--outDir', new File(typescriptOut, typescriptRoot).absolutePath
|
||||
args '--project', typescriptRootFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processResources.dependsOn compileTypescript
|
||||
|
||||
def typedocOut = new File(buildDir, 'typedoc')
|
||||
|
||||
task typedoc {
|
||||
inputs.dir typescriptRootFile
|
||||
outputs.dir typedocOut
|
||||
|
||||
doFirst {
|
||||
project.delete {
|
||||
delete typedocOut
|
||||
}
|
||||
typedocOut.mkdirs()
|
||||
|
||||
project.exec {
|
||||
workingDir typescriptRootFile
|
||||
executable 'typedoc'
|
||||
|
||||
args '--out', typedocOut.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property 'version', mod_version
|
||||
inputs.property 'name', rootProject.name
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'fabric.mod.json'
|
||||
expand 'version': mod_version, 'name': rootProject.name
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude 'fabric.mod.json'
|
||||
}
|
||||
|
||||
exclude typescriptRoot + '/**/*.ts'
|
||||
exclude typescriptRoot + '/types'
|
||||
|
||||
for (JNIPlatform platform : jniPlatforms) {
|
||||
def buildDir = new File(cDir, "build-${platform.name}")
|
||||
if (!buildDir.exists()) {
|
||||
buildDir.mkdir()
|
||||
}
|
||||
|
||||
def file = new File(buildDir, 'libscriptcraft' + platform.libExtension)
|
||||
inputs.files file
|
||||
|
||||
from(file.absolutePath) {
|
||||
into new File('natives', platform.name).path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||
// if it is present.
|
||||
// If you remove this task, sources will not be generated.
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
task typedocJar(type: Jar) {
|
||||
classifier 'typedoc'
|
||||
from typedocOut
|
||||
}
|
||||
|
||||
typedocJar.dependsOn typedoc
|
||||
|
||||
artifacts {
|
||||
archives sourcesJar
|
||||
archives typedocJar
|
||||
}
|
||||
|
||||
jar {
|
||||
from 'LICENSE'
|
||||
}
|
||||
|
||||
if (project.hasProperty('curseforge.api_key')) {
|
||||
curseforge {
|
||||
apiKey = project.getProperty('curseforge.api_key')
|
||||
project {
|
||||
id = project.curseforge_id
|
||||
changelog = 'A changelog can be found at https://gitea.thebrokenrail.com/TheBrokenRail/ScriptCraft/src/branch/master/CHANGELOG.md'
|
||||
releaseType = 'release'
|
||||
addGameVersion project.simple_minecraft_version
|
||||
addGameVersion 'Fabric'
|
||||
mainArtifact(remapJar) {
|
||||
displayName = "ScriptCraft v${mod_version} for ${project.minecraft_version}"
|
||||
}
|
||||
afterEvaluate {
|
||||
uploadTask.dependsOn('remapJar')
|
||||
}
|
||||
relations {
|
||||
requiredDependency 'fabric-api'
|
||||
}
|
||||
}
|
||||
options {
|
||||
forgeGradleIntegration = false
|
||||
}
|
||||
}
|
||||
}
|
7
cmake/linux_i686_toolchain.cmake
Normal file
7
cmake/linux_i686_toolchain.cmake
Normal file
@ -0,0 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
|
||||
set(CMAKE_C_FLAGS "-m32 -march=i686")
|
||||
set(CMAKE_CXX_FLAGS "-m32 -march=i686")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-m32 -march=i686")
|
14
cmake/windows_i686_toolchain.cmake
Normal file
14
cmake/windows_i686_toolchain.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86)
|
||||
set(TOOLCHAIN_PREFIX i686-w64-mingw32)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
|
||||
set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran)
|
||||
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
14
cmake/windows_x86_64_toolchain.cmake
Normal file
14
cmake/windows_x86_64_toolchain.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR amd64)
|
||||
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
|
||||
set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran)
|
||||
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
21
gradle.properties
Normal file
21
gradle.properties
Normal file
@ -0,0 +1,21 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs = -Xmx1G
|
||||
|
||||
typescript_root = scriptcraft
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/use
|
||||
minecraft_version = 1.15.2
|
||||
curseforge_id = TBD
|
||||
simple_minecraft_version = 1.15.2
|
||||
yarn_build = 15
|
||||
fabric_loader_version = 0.8.2+build.194
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0.0
|
||||
maven_group = com.thebrokenrail
|
||||
archives_base_name = scriptcraft
|
||||
|
||||
# Dependencies
|
||||
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
||||
fabric_api_version = 0.5.12+build.296-1.15
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Sat Feb 29 21:58:32 EST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
188
gradlew
vendored
Executable file
188
gradlew
vendored
Executable file
@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
100
gradlew.bat
vendored
Normal file
100
gradlew.bat
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
33
scripts/download-jni-headers.sh
Executable file
33
scripts/download-jni-headers.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ -e jdk.tar.gz ]; then
|
||||
rm -f jdk.tar.gz
|
||||
fi
|
||||
curl -o jdk.tar.gz https://hg.openjdk.java.net/jdk/jdk11/archive/tip.tar.gz
|
||||
|
||||
if [ -d ../src/main/c/jni ]; then
|
||||
rm -rf ../src/main/c/jni
|
||||
fi
|
||||
mkdir ../src/main/c/jni
|
||||
|
||||
if [ -d jdk ]; then
|
||||
rm -rf jdk
|
||||
fi
|
||||
mkdir jdk
|
||||
tar -zxf jdk.tar.gz --strip-components=1 -C jdk
|
||||
|
||||
rm -f jdk.tar.gz
|
||||
|
||||
cp -r jdk/src/java.base/share/native/include/. ../src/main/c/jni
|
||||
|
||||
copy_jni() {
|
||||
mkdir "../src/main/c/jni/$1"
|
||||
cp -r "jdk/src/java.base/$1/native/include/." "../src/main/c/jni/$1"
|
||||
}
|
||||
|
||||
copy_jni unix
|
||||
copy_jni windows
|
||||
|
||||
rm -rf jdk
|
17
scripts/download-quickjs.sh
Executable file
17
scripts/download-quickjs.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
QUICKJS_VERSION='2020-04-12'
|
||||
|
||||
set -e
|
||||
|
||||
if [ -e quickjs.tar.xz ]; then
|
||||
rm -f quickjs.tar.xz
|
||||
fi
|
||||
curl -L -o quickjs.tar.xz https://bellard.org/quickjs/quickjs-${QUICKJS_VERSION}.tar.xz
|
||||
|
||||
if [ -d ../src/main/c/quickjs ]; then
|
||||
rm -rf ../src/main/c/quickjs
|
||||
fi
|
||||
mkdir ../src/main/c/quickjs
|
||||
tar -Jxf quickjs.tar.xz --strip-components=1 -C ../src/main/c/quickjs
|
||||
rm -f quickjs.tar.xz
|
6
scripts/setup.sh
Executable file
6
scripts/setup.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
./download-quickjs.sh
|
||||
./download-jni-headers.sh
|
12
settings.gradle
Normal file
12
settings.gradle
Normal file
@ -0,0 +1,12 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'ScriptCraft'
|
31
src/main/c/CMakeLists.txt
Normal file
31
src/main/c/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(scriptcraft C)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast")
|
||||
|
||||
add_library(
|
||||
scriptcraft
|
||||
SHARED
|
||||
quickjs/quickjs.c
|
||||
quickjs/libregexp.c
|
||||
quickjs/libunicode.c
|
||||
quickjs/cutils.c
|
||||
quickjs/libbf.c
|
||||
console.c
|
||||
com_thebrokenrail_scriptcraft_quickjs_QuickJS.c
|
||||
)
|
||||
|
||||
file(STRINGS "quickjs/VERSION" QUICKJS_VERSION)
|
||||
target_compile_definitions(scriptcraft PUBLIC -D_GNU_SOURCE PUBLIC -DCONFIG_VERSION=\"${QUICKJS_VERSION}\" -DCONFIG_BIGNUM -DDUMP_LEAKS)
|
||||
|
||||
target_include_directories(scriptcraft PUBLIC quickjs)
|
||||
|
||||
include_directories(jni)
|
||||
if(UNIX)
|
||||
include_directories(jni/unix)
|
||||
elseif(WIN32)
|
||||
include_directories(jni/windows)
|
||||
endif()
|
||||
|
||||
target_link_libraries(scriptcraft m pthread)
|
62
src/main/c/asprintf.h
Normal file
62
src/main/c/asprintf.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef ASPRINTF_H
|
||||
#define ASPRINTF_H
|
||||
|
||||
#if defined(__GNUC__) && ! defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE /* needed for (v)asprintf, affects '#include <stdio.h>' */
|
||||
#endif
|
||||
#include <stdio.h> /* needed for vsnprintf */
|
||||
#include <stdlib.h> /* needed for malloc, free */
|
||||
#include <stdarg.h> /* needed for va_* */
|
||||
|
||||
/*
|
||||
* vscprintf:
|
||||
* MSVC implements this as _vscprintf, thus we just 'symlink' it here
|
||||
* GNU-C-compatible compilers do not implement this, thus we implement it here
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define vscprintf _vscprintf
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
inline static int vscprintf(const char *format, va_list ap) {
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
int retval = vsnprintf(NULL, 0, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* asprintf, vasprintf:
|
||||
* MSVC does not implement these, thus we implement them here
|
||||
* GNU-C-compatible compilers implement these with the same names, thus we
|
||||
* don't have to do anything
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
inline static int vasprintf(char **strp, const char *format, va_list ap) {
|
||||
int len = vscprintf(format, ap);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
char *str = (char*)malloc((size_t) len + 1);
|
||||
if (!str)
|
||||
return -1;
|
||||
int retval = vsnprintf(str, len + 1, format, ap);
|
||||
if (retval == -1) {
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
*strp = str;
|
||||
return retval;
|
||||
}
|
||||
|
||||
inline static int asprintf(char **strp, const char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int retval = vasprintf(strp, format, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ASPRINTF_H
|
464
src/main/c/com_thebrokenrail_scriptcraft_quickjs_QuickJS.c
Normal file
464
src/main/c/com_thebrokenrail_scriptcraft_quickjs_QuickJS.c
Normal file
@ -0,0 +1,464 @@
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cutils.h>
|
||||
#include <quickjs.h>
|
||||
|
||||
#include "com_thebrokenrail_scriptcraft_quickjs_QuickJS.h"
|
||||
#include "console.h"
|
||||
|
||||
static JavaVM *jvm;
|
||||
|
||||
static void *get_pointer(JNIEnv *env, jobject obj, char *name) {
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/QuickJS");
|
||||
jfieldID field = (*env)->GetFieldID(env, clazz, name, "J");
|
||||
return (void *) (long) (*env)->GetLongField(env, obj, field);
|
||||
}
|
||||
|
||||
static void set_pointer(JNIEnv *env, jobject obj, char *name, void *value) {
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/QuickJS");
|
||||
jfieldID field = (*env)->GetFieldID(env, clazz, name, "J");
|
||||
(*env)->SetLongField(env, obj, field, (jlong) (long) value);
|
||||
}
|
||||
|
||||
static char *js_module_normalize_name(JSContext *ctx, const char *base_name, const char *name, void *opaque) {
|
||||
JNIEnv *env;
|
||||
(*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
|
||||
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/QuickJS");
|
||||
jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "normalizeModule", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
jstring java_name = (*env)->NewStringUTF(env, name);
|
||||
jstring java_base_name = (*env)->NewStringUTF(env, base_name);
|
||||
jstring java_string = (jstring) (*env)->CallStaticObjectMethod(env, clazz, methodID, java_base_name, java_name);
|
||||
(*env)->DeleteLocalRef(env, java_name);
|
||||
(*env)->DeleteLocalRef(env, java_base_name);
|
||||
|
||||
if (java_string) {
|
||||
const char *native_string = (*env)->GetStringUTFChars(env, java_string, 0);
|
||||
char *new_string = js_strdup(ctx, native_string);
|
||||
(*env)->ReleaseStringUTFChars(env, java_string, native_string);
|
||||
|
||||
(*env)->DeleteLocalRef(env, java_string);
|
||||
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
return new_string;
|
||||
} else {
|
||||
jthrowable err = (*env)->ExceptionOccurred(env);
|
||||
if (err) {
|
||||
jmethodID to_string = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Object"), "toString", "()Ljava/lang/String;");
|
||||
jstring java_string = (jstring) (*env)->CallObjectMethod(env, err, to_string);
|
||||
const char *str = (*env)->GetStringUTFChars(env, java_string, 0);
|
||||
|
||||
JS_ThrowReferenceError(ctx, "could not normalize module name '%s': %s", name, str);
|
||||
|
||||
(*env)->ExceptionClear(env);
|
||||
} else {
|
||||
JS_ThrowReferenceError(ctx, "could not normalize module name '%s'", name);
|
||||
}
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, JS_BOOL use_realpath, JS_BOOL is_main) {
|
||||
JSModuleDef *m;
|
||||
char buf[PATH_MAX + 16];
|
||||
JSValue meta_obj;
|
||||
JSAtom module_name_atom;
|
||||
const char *module_name;
|
||||
|
||||
assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE);
|
||||
m = JS_VALUE_GET_PTR(func_val);
|
||||
|
||||
module_name_atom = JS_GetModuleName(ctx, m);
|
||||
module_name = JS_AtomToCString(ctx, module_name_atom);
|
||||
JS_FreeAtom(ctx, module_name_atom);
|
||||
if (!module_name) {
|
||||
return -1;
|
||||
}
|
||||
if (!strchr(module_name, ':')) {
|
||||
strcpy(buf, "file://");
|
||||
}
|
||||
pstrcpy(buf, sizeof(buf), module_name);
|
||||
JS_FreeCString(ctx, module_name);
|
||||
|
||||
meta_obj = JS_GetImportMeta(ctx, m);
|
||||
if (JS_IsException(meta_obj)) {
|
||||
return -1;
|
||||
}
|
||||
JS_DefinePropertyValueStr(ctx, meta_obj, "url", JS_NewString(ctx, buf), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, meta_obj, "main", JS_NewBool(ctx, is_main), JS_PROP_C_W_E);
|
||||
JS_FreeValue(ctx, meta_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *js_load_file(JSContext *ctx, const char *filename) {
|
||||
JNIEnv *env;
|
||||
(*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
|
||||
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/QuickJS");
|
||||
jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "loadModule", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
jstring java_filename = (*env)->NewStringUTF(env, filename);
|
||||
jstring java_string = (jstring) (*env)->CallStaticObjectMethod(env, clazz, methodID, java_filename);
|
||||
(*env)->DeleteLocalRef(env, java_filename);
|
||||
|
||||
if (java_string) {
|
||||
const char *native_string = (*env)->GetStringUTFChars(env, java_string, 0);
|
||||
char *new_string = js_strdup(ctx, native_string);
|
||||
(*env)->ReleaseStringUTFChars(env, java_string, native_string);
|
||||
|
||||
(*env)->DeleteLocalRef(env, java_string);
|
||||
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
return new_string;
|
||||
} else {
|
||||
jthrowable err = (*env)->ExceptionOccurred(env);
|
||||
if (err) {
|
||||
jmethodID to_string = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Object"), "toString", "()Ljava/lang/String;");
|
||||
jstring java_string = (jstring) (*env)->CallObjectMethod(env, err, to_string);
|
||||
const char *str = (*env)->GetStringUTFChars(env, java_string, 0);
|
||||
|
||||
JS_ThrowReferenceError(ctx, "could not load module filename '%s': %s", filename, str);
|
||||
|
||||
(*env)->ExceptionClear(env);
|
||||
} else {
|
||||
JS_ThrowReferenceError(ctx, "could not load module filename '%s'", filename);
|
||||
}
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque) {
|
||||
JSModuleDef *m;
|
||||
|
||||
char *buf;
|
||||
JSValue func_val;
|
||||
|
||||
buf = js_load_file(ctx, module_name);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int is_json = has_suffix(module_name, ".json");
|
||||
if (is_json) {
|
||||
asprintf(&buf, "export default JSON.parse(`%s`);", buf);
|
||||
}
|
||||
|
||||
/* compile the module */
|
||||
func_val = JS_Eval(ctx, buf, strlen(buf), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
js_free(ctx, buf);
|
||||
if (JS_IsException(func_val)) {
|
||||
return NULL;
|
||||
}
|
||||
/* XXX: could propagate the exception */
|
||||
js_module_set_import_meta(ctx, func_val, 1, 0);
|
||||
/* the module is already referenced, so we must free it */
|
||||
m = JS_VALUE_GET_PTR(func_val);
|
||||
JS_FreeValue(ctx, func_val);
|
||||
return m;
|
||||
}
|
||||
|
||||
static void throw_exception(JNIEnv *env, char *message) {
|
||||
jclass exception_clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/JSException");
|
||||
(*env)->ThrowNew(env, exception_clazz, message);
|
||||
}
|
||||
|
||||
static JSClassID JS_CLASS_JAVA_OBJECT_ID;
|
||||
|
||||
typedef struct java_object_data {
|
||||
jobject obj;
|
||||
} java_object_data;
|
||||
|
||||
static void java_object_finalizer(JSRuntime *rt, JSValue val) {
|
||||
java_object_data *data = JS_GetOpaque(val, JS_CLASS_JAVA_OBJECT_ID);
|
||||
if (data) {
|
||||
JNIEnv *env;
|
||||
(*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
|
||||
|
||||
(*env)->DeleteGlobalRef(env, data->obj);
|
||||
js_free_rt(rt, data);
|
||||
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef JS_CLASS_JAVA_OBJECT = {
|
||||
"JavaObject",
|
||||
.finalizer = java_object_finalizer
|
||||
};
|
||||
|
||||
static JSValue java_object_to_js_object(JNIEnv *env, JSContext *ctx, jobject obj) {
|
||||
JSValue out = JS_NULL;
|
||||
|
||||
jclass boolean_clazz = (*env)->FindClass(env, "java/lang/Boolean");
|
||||
jclass double_clazz = (*env)->FindClass(env, "java/lang/Double");
|
||||
jclass string_clazz = (*env)->FindClass(env, "java/lang/String");
|
||||
jclass array_clazz = (*env)->FindClass(env, "[Ljava/lang/Object;");
|
||||
if ((*env)->IsInstanceOf(env, obj, string_clazz)) {
|
||||
const char *native_string = (*env)->GetStringUTFChars(env, (jstring) obj, 0);
|
||||
out = JS_NewString(ctx, native_string);
|
||||
(*env)->ReleaseStringUTFChars(env, (jstring) obj, native_string);
|
||||
} else if ((*env)->IsInstanceOf(env, obj, boolean_clazz)) {
|
||||
jmethodID boolean_methodID = (*env)->GetMethodID(env, boolean_clazz, "booleanValue", "()Z");
|
||||
jboolean val = (*env)->CallBooleanMethod(env, obj, boolean_methodID);
|
||||
out = JS_NewBool(ctx, val);
|
||||
} else if ((*env)->IsInstanceOf(env, obj, double_clazz)) {
|
||||
jmethodID double_methodID = (*env)->GetMethodID(env, double_clazz, "doubleValue", "()D");
|
||||
jdouble val = (*env)->CallDoubleMethod(env, obj, double_methodID);
|
||||
out = JS_NewFloat64(ctx, val);
|
||||
} else if ((*env)->IsInstanceOf(env, obj, array_clazz)) {
|
||||
out = JS_NewArray(ctx);
|
||||
|
||||
int length = (*env)->GetArrayLength(env, (jobjectArray) obj);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
JS_SetPropertyUint32(ctx, out, i, java_object_to_js_object(env, ctx, (*env)->GetObjectArrayElement(env, obj, i)));
|
||||
}
|
||||
} else {
|
||||
java_object_data *data = js_mallocz(ctx, sizeof (java_object_data));
|
||||
data->obj = (*env)->NewGlobalRef(env, obj);
|
||||
out = JS_NewObjectClass(ctx, JS_CLASS_JAVA_OBJECT_ID);
|
||||
JS_SetOpaque(out, data);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static jobject js_object_to_java_object(JNIEnv *env, JSContext *ctx, JSValue input) {
|
||||
jobject obj = NULL;
|
||||
if (JS_IsBool(input)) {
|
||||
int val = JS_ToBool(ctx, input);
|
||||
|
||||
jclass boolean_clazz = (*env)->FindClass(env, "java/lang/Boolean");
|
||||
jmethodID methodID = (*env)->GetMethodID(env, boolean_clazz, "<init>", "(Z)V");
|
||||
obj = (*env)->NewObject(env, boolean_clazz, methodID, val);
|
||||
} else if (JS_IsNumber(input)) {
|
||||
double val;
|
||||
JS_ToFloat64(ctx, &val, input);
|
||||
|
||||
jclass double_clazz = (*env)->FindClass(env, "java/lang/Double");
|
||||
jmethodID methodID = (*env)->GetMethodID(env, double_clazz, "<init>", "(D)V");
|
||||
obj = (*env)->NewObject(env, double_clazz, methodID, val);
|
||||
} else if (JS_IsString(input)) {
|
||||
const char *val = JS_ToCString(ctx, input);
|
||||
obj = (*env)->NewStringUTF(env, val);
|
||||
JS_FreeCString(ctx, val);
|
||||
} else if (JS_IsArray(ctx, input)) {
|
||||
uint32_t length;
|
||||
|
||||
JSValue js_length = JS_GetPropertyStr(ctx, input, "length");
|
||||
JS_ToUint32(ctx, &length, js_length);
|
||||
JS_FreeValue(ctx, js_length);
|
||||
|
||||
jclass obj_clazz = (*env)->FindClass(env, "java/lang/Object");
|
||||
jobjectArray arr = (*env)->NewObjectArray(env, length, obj_clazz, NULL);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSValue element = JS_GetPropertyUint32(ctx, input, i);
|
||||
jobject obj = js_object_to_java_object(env, ctx, element);
|
||||
(*env)->SetObjectArrayElement(env, arr, i - 1, obj);
|
||||
(*env)->DeleteLocalRef(env, obj);
|
||||
JS_FreeValue(ctx, element);
|
||||
}
|
||||
|
||||
obj = arr;
|
||||
} else {
|
||||
java_object_data *data = JS_GetOpaque(input, JS_CLASS_JAVA_OBJECT_ID);
|
||||
if (data) {
|
||||
obj = (*env)->NewLocalRef(env, data->obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_bridge(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
JNIEnv *env;
|
||||
(*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
|
||||
|
||||
jclass obj_clazz = (*env)->FindClass(env, "java/lang/Object");
|
||||
jobjectArray arr = (*env)->NewObjectArray(env, argc - 1, obj_clazz, NULL);
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
jobject obj = js_object_to_java_object(env, ctx, argv[i]);
|
||||
(*env)->SetObjectArrayElement(env, arr, i - 1, obj);
|
||||
(*env)->DeleteLocalRef(env, obj);
|
||||
}
|
||||
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/bridge/Bridges");
|
||||
jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "useBridge", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
|
||||
const char *js_bridge_name = JS_ToCString(ctx, argv[0]);
|
||||
jstring bridge_name = (*env)->NewStringUTF(env, js_bridge_name);
|
||||
|
||||
jobject out = (*env)->CallStaticObjectMethod(env, clazz, methodID, bridge_name, arr);
|
||||
|
||||
(*env)->DeleteLocalRef(env, bridge_name);
|
||||
(*env)->DeleteLocalRef(env, arr);
|
||||
|
||||
JSValue js_out;
|
||||
if (out) {
|
||||
js_out = java_object_to_js_object(env, ctx, out);
|
||||
(*env)->DeleteLocalRef(env, out);
|
||||
} else {
|
||||
jthrowable err = (*env)->ExceptionOccurred(env);
|
||||
if (err) {
|
||||
jmethodID to_string = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Object"), "toString", "()Ljava/lang/String;");
|
||||
jstring java_string = (jstring) (*env)->CallObjectMethod(env, err, to_string);
|
||||
const char *str = (*env)->GetStringUTFChars(env, java_string, 0);
|
||||
|
||||
js_out = JS_ThrowReferenceError(ctx, "unable to use bridge '%s': %s", js_bridge_name, str);
|
||||
|
||||
(*env)->ExceptionClear(env);
|
||||
} else {
|
||||
js_out = JS_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, js_bridge_name);
|
||||
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
|
||||
return js_out;
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_bridge(JNIEnv *env, jobject this_val, jstring bridge_name, jobjectArray arr) {
|
||||
JSContext *ctx = (JSContext *) get_pointer(env, this_val, "ctx");
|
||||
|
||||
int length = (*env)->GetArrayLength(env, arr);
|
||||
JSValue args[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
args[i] = java_object_to_js_object(env, ctx, (*env)->GetObjectArrayElement(env, arr, i));
|
||||
}
|
||||
|
||||
JSValue global_obj = JS_GetGlobalObject(ctx);
|
||||
JSValue scriptcraft_obj = JS_GetPropertyStr(ctx, global_obj, "__scriptcraft__");
|
||||
JSValue bridges = JS_GetPropertyStr(ctx, scriptcraft_obj, "bridges");
|
||||
|
||||
const char *native_string = (*env)->GetStringUTFChars(env, bridge_name, 0);
|
||||
JSValue bridge = JS_GetPropertyStr(ctx, bridges, native_string);
|
||||
(*env)->ReleaseStringUTFChars(env, bridge_name, native_string);
|
||||
|
||||
JSValue out;
|
||||
if (JS_IsFunction(ctx, bridge)) {
|
||||
out = JS_Call(ctx, bridge, global_obj, length, args);
|
||||
if (JS_IsException(out)) {
|
||||
js_std_dump_error(ctx);
|
||||
out = JS_NULL;
|
||||
}
|
||||
} else {
|
||||
out = JS_NULL;
|
||||
throw_exception(env, "Invalid Bridge");
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
JS_FreeValue(ctx, args[i]);
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, bridge);
|
||||
JS_FreeValue(ctx, bridges);
|
||||
JS_FreeValue(ctx, scriptcraft_obj);
|
||||
JS_FreeValue(ctx, global_obj);
|
||||
|
||||
jobject java_out = js_object_to_java_object(env, ctx, out);
|
||||
JS_FreeValue(ctx, out);
|
||||
|
||||
return java_out;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_init(JNIEnv *env, jobject this_val) {
|
||||
jint rc = (*env)->GetJavaVM(env, &jvm);
|
||||
if (rc != JNI_OK) {
|
||||
throw_exception(env, "qjs: unable to cache JavaVM");
|
||||
return;
|
||||
}
|
||||
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
throw_exception(env, "qjs: cannot allocate JS runtime");
|
||||
return;
|
||||
}
|
||||
JSContext *ctx = JS_NewContext(rt);
|
||||
if (!ctx) {
|
||||
throw_exception(env, "qjs: cannot allocate JS context");
|
||||
return;
|
||||
}
|
||||
|
||||
JS_SetModuleLoaderFunc(rt, js_module_normalize_name, js_module_loader, NULL);
|
||||
|
||||
js_console_init(ctx);
|
||||
|
||||
JSValue global_obj = JS_GetGlobalObject(ctx);
|
||||
|
||||
JSValue scriptcraft_obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, scriptcraft_obj, "bridges", JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, scriptcraft_obj, "useBridge", JS_NewCFunction(ctx, js_bridge, "useBridge", 1));
|
||||
JS_SetPropertyStr(ctx, global_obj, "__scriptcraft__", scriptcraft_obj);
|
||||
|
||||
JS_FreeValue(ctx, global_obj);
|
||||
|
||||
JS_NewClassID(&JS_CLASS_JAVA_OBJECT_ID);
|
||||
JS_NewClass(JS_GetRuntime(ctx), JS_CLASS_JAVA_OBJECT_ID, &JS_CLASS_JAVA_OBJECT);
|
||||
JS_SetClassProto(ctx, JS_CLASS_JAVA_OBJECT_ID, JS_NewObject(ctx));
|
||||
|
||||
set_pointer(env, this_val, "rt", rt);
|
||||
set_pointer(env, this_val, "ctx", ctx);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_free(JNIEnv *env, jobject this_val) {
|
||||
JSContext *ctx = (JSContext *) get_pointer(env, this_val, "ctx");
|
||||
JSRuntime *rt = (JSRuntime *) get_pointer(env, this_val, "rt");
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
}
|
||||
|
||||
static int eval_buf(JNIEnv *env, JSContext *ctx, const void *buf, int buf_len, const char *filename, int eval_flags) {
|
||||
JSValue val;
|
||||
int ret;
|
||||
|
||||
if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
|
||||
/* for the modules, we compile then run to be able to set
|
||||
import.meta */
|
||||
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
if (!JS_IsException(val)) {
|
||||
js_module_set_import_meta(ctx, val, TRUE, TRUE);
|
||||
val = JS_EvalFunction(ctx, val);
|
||||
}
|
||||
} else {
|
||||
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
|
||||
}
|
||||
if (JS_IsException(val)) {
|
||||
js_std_dump_error(ctx);
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_run(JNIEnv *env, jobject this_val, jstring data) {
|
||||
JSContext *ctx = (JSContext *) get_pointer(env, this_val, "ctx");
|
||||
|
||||
const char *native_string = (*env)->GetStringUTFChars(env, data, 0);
|
||||
eval_buf(env, ctx, native_string, strlen(native_string), "<init>", JS_EVAL_TYPE_MODULE);
|
||||
(*env)->ReleaseStringUTFChars(env, data, native_string);
|
||||
}
|
||||
|
||||
void print_data(char *data, int err) {
|
||||
JNIEnv *env;
|
||||
(*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
|
||||
|
||||
jclass clazz = (*env)->FindClass(env, "com/thebrokenrail/scriptcraft/quickjs/QuickJS");
|
||||
jmethodID print_methodID = (*env)->GetStaticMethodID(env, clazz, "print", "(Ljava/lang/String;Z)V");
|
||||
jstring str = (*env)->NewStringUTF(env, data);
|
||||
(*env)->CallStaticVoidMethod(env, clazz, print_methodID, str, err);
|
||||
(*env)->DeleteLocalRef(env, str);
|
||||
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
}
|
47
src/main/c/com_thebrokenrail_scriptcraft_quickjs_QuickJS.h
Normal file
47
src/main/c/com_thebrokenrail_scriptcraft_quickjs_QuickJS.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class com_thebrokenrail_scriptcraft_quickjs_QuickJS */
|
||||
|
||||
#ifndef _Included_com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
#define _Included_com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
* Method: init
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_init
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
* Method: free
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_free
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
* Method: bridge
|
||||
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_bridge
|
||||
(JNIEnv *, jobject, jstring, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_thebrokenrail_scriptcraft_quickjs_QuickJS
|
||||
* Method: run
|
||||
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_thebrokenrail_scriptcraft_quickjs_QuickJS_run
|
||||
(JNIEnv *, jobject, jstring);
|
||||
|
||||
void print_data(char *data, int err);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
119
src/main/c/console.c
Normal file
119
src/main/c/console.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "asprintf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <quickjs.h>
|
||||
|
||||
#include "com_thebrokenrail_scriptcraft_quickjs_QuickJS.h"
|
||||
|
||||
static JSValue js_print_internal(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int err, int prepend_throw) {
|
||||
int i;
|
||||
char *out;
|
||||
if (prepend_throw) {
|
||||
out = "Throw: ";
|
||||
} else {
|
||||
out = "";
|
||||
}
|
||||
const char *str;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (i != 0) {
|
||||
asprintf(&out, "%s ", out);
|
||||
}
|
||||
str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
asprintf(&out, "%s%s", out, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
print_data(out, err);
|
||||
free(out);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
return js_print_internal(ctx, this_val, argc, argv, 0, 0);
|
||||
}
|
||||
|
||||
static JSValue js_print_err(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
return js_print_internal(ctx, this_val, argc, argv, 1, 0);
|
||||
}
|
||||
|
||||
static JSValue js_assert(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
int i;
|
||||
const char *str;
|
||||
|
||||
if (argc < 1) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
int bool_val = JS_ToBool(ctx, argv[0]);
|
||||
if (bool_val == -1) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (!bool_val) {
|
||||
char *out = "Assertion failed";
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (i != 1) {
|
||||
asprintf(&out, "%s ", out);
|
||||
} else {
|
||||
asprintf(&out, "%s: ", out);
|
||||
}
|
||||
str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
asprintf(&out, "%s%s", out, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
print_data(out, 1);
|
||||
free(out);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_console_funcs[] = {
|
||||
JS_CFUNC_DEF("log", 1, js_print),
|
||||
JS_CFUNC_DEF("info", 1, js_print),
|
||||
JS_CFUNC_DEF("warn", 1, js_print),
|
||||
JS_CFUNC_DEF("error", 1, js_print_err),
|
||||
JS_CFUNC_DEF("dir", 1, js_print),
|
||||
JS_CFUNC_DEF("debug", 1, js_print),
|
||||
JS_CFUNC_DEF("trace", 1, js_print),
|
||||
JS_CFUNC_DEF("assert", 1, js_assert),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Console", JS_PROP_CONFIGURABLE)
|
||||
};
|
||||
|
||||
void js_console_init(JSContext *ctx) {
|
||||
JSValue global_obj, console;
|
||||
|
||||
/* XXX: should these global definitions be enumerable? */
|
||||
global_obj = JS_GetGlobalObject(ctx);
|
||||
|
||||
console = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, console, js_console_funcs, sizeof js_console_funcs / sizeof (JSCFunctionListEntry));
|
||||
JS_SetPropertyStr(ctx, global_obj, "console", console);
|
||||
|
||||
JS_FreeValue(ctx, global_obj);
|
||||
}
|
||||
|
||||
void js_std_dump_error(JSContext *ctx) {
|
||||
JSValue exception_val, val;
|
||||
const char *stack;
|
||||
int is_error;
|
||||
|
||||
exception_val = JS_GetException(ctx);
|
||||
is_error = JS_IsError(ctx, exception_val);
|
||||
js_print_internal(ctx, JS_NULL, 1, (JSValueConst *) &exception_val, 1, !is_error);
|
||||
if (is_error) {
|
||||
val = JS_GetPropertyStr(ctx, exception_val, "stack");
|
||||
if (JS_ToBool(ctx, val)) {
|
||||
stack = JS_ToCString(ctx, val);
|
||||
print_data((char *) stack, 1);
|
||||
JS_FreeCString(ctx, stack);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
}
|
||||
JS_FreeValue(ctx, exception_val);
|
||||
}
|
5
src/main/c/console.h
Normal file
5
src/main/c/console.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include <quickjs.h>
|
||||
|
||||
void js_console_init(JSContext *ctx);
|
||||
|
||||
void js_std_dump_error(JSContext *ctx);
|
15
src/main/java/com/thebrokenrail/scriptcraft/Demo.java
Normal file
15
src/main/java/com/thebrokenrail/scriptcraft/Demo.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.thebrokenrail.scriptcraft;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.util.ScriptCraftEntrypoint;
|
||||
|
||||
public class Demo implements ScriptCraftEntrypoint {
|
||||
@Override
|
||||
public String getModID() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModIndex() {
|
||||
return "index.js";
|
||||
}
|
||||
}
|
35
src/main/java/com/thebrokenrail/scriptcraft/ScriptCraft.java
Normal file
35
src/main/java/com/thebrokenrail/scriptcraft/ScriptCraft.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.thebrokenrail.scriptcraft;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJS;
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJSManager;
|
||||
import com.thebrokenrail.scriptcraft.util.ScriptCraftEntrypoint;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ScriptCraft implements ModInitializer {
|
||||
public static final String NAMESPACE = "scriptcraft";
|
||||
public static final Pattern MOD_ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{1,63}");
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
QuickJSManager.init(new QuickJSManager.Task() {
|
||||
@Override
|
||||
protected Object run(QuickJS quickjs) {
|
||||
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntrypoint.class);
|
||||
for (ScriptCraftEntrypoint mod : mods) {
|
||||
if (MOD_ID_PATTERN.matcher(mod.getModID()).matches()) {
|
||||
if (mod.shouldAutoLoad()) {
|
||||
quickjs.run("import '" + mod.getModID() + "';");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Invalid Mod ID: " + mod.getModID());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.thebrokenrail.scriptcraft.api;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.util.Util;
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJSManager;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CustomBlock extends Block {
|
||||
private final Identifier id;
|
||||
|
||||
public CustomBlock(Settings settings, Identifier id) {
|
||||
super(settings);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
return Util.getEnumValue(ActionResult.class, (String) QuickJSManager.bridge("CustomBlock.onUse", id.toString(), world, state, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), hit.getSide().name(), player, hand.name()), ActionResult.PASS);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.thebrokenrail.scriptcraft.api;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.util.Util;
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJSManager;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CustomItem extends Item {
|
||||
private final Identifier id;
|
||||
|
||||
public CustomItem(Settings settings, Identifier id) {
|
||||
super(settings);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||
ItemStack stack = user.getStackInHand(hand);
|
||||
ActionResult result = Util.getEnumValue(ActionResult.class, (String) QuickJSManager.bridge("CustomItem.onUse", id.toString(), world, user, hand.name()), ActionResult.PASS);
|
||||
return new TypedActionResult<>(result, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||
return Util.getEnumValue(ActionResult.class, (String) QuickJSManager.bridge("CustomItem.onUseOnBlock", id.toString(), context.getWorld(), (double) context.getBlockPos().getX(), (double) context.getBlockPos().getY(), (double) context.getBlockPos().getZ(), context.getSide().name(), context.getPlayer(), context.getHand().name()), ActionResult.PASS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand) {
|
||||
return Util.toBoolean(QuickJSManager.bridge("CustomItem.onUseOnEntity", id.toString(), user, entity, hand.name()), false);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.fabricmc.fabric.api.block.FabricBlockSettings;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.block.MaterialColor;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
class BlockSettingsBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("BlockSettings.create", args -> {
|
||||
try {
|
||||
String materialID = ((String) args[0]).toUpperCase(Locale.ROOT);
|
||||
Material material = (Material) Material.class.getField(materialID).get(null);
|
||||
|
||||
MaterialColor materialColor;
|
||||
if (args[1] != null) {
|
||||
String materialColorID = ((String) args[1]).toUpperCase(Locale.ROOT);
|
||||
materialColor = (MaterialColor) MaterialColor.class.getField(materialColorID).get(null);
|
||||
} else {
|
||||
materialColor = material.getColor();
|
||||
}
|
||||
|
||||
FabricBlockSettings settings = FabricBlockSettings.of(material, materialColor);
|
||||
|
||||
settings.hardness(((Double) args[2]).floatValue());
|
||||
settings.resistance(((Double) args[3]).floatValue());
|
||||
|
||||
return settings.build();
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
class BlockStateBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("BlockState.getDefaultState", args -> Registry.BLOCK.get(new Identifier((String) args[0])).getDefaultState());
|
||||
Bridges.addBridge("BlockState.getBlock", args -> Registry.BLOCK.getId(((BlockState) args[0]).getBlock()).toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
public interface Bridge {
|
||||
Object use(Object... args);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJS;
|
||||
import com.thebrokenrail.scriptcraft.quickjs.QuickJSManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Bridges {
|
||||
private static final HashMap<String, Bridge> bridges = new HashMap<>();
|
||||
|
||||
public static void addBridge(String name, Bridge bridge) {
|
||||
bridges.put(name, bridge);
|
||||
}
|
||||
|
||||
public static Object useBridge(String name, Object... args) {
|
||||
QuickJSManager.Task task = new QuickJSManager.Task() {
|
||||
@Override
|
||||
protected Object run(QuickJS quickjs) {
|
||||
if (bridges.containsKey(name)) {
|
||||
return bridges.get(name).use(args);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid Bridge: '" + name + '\'');
|
||||
}
|
||||
}
|
||||
};
|
||||
return QuickJSManager.sendTaskFromQuickJS(task);
|
||||
}
|
||||
|
||||
static {
|
||||
BlockSettingsBridges.register();
|
||||
RegistryBridge.register();
|
||||
ItemStackBridges.register();
|
||||
LivingEntityBridges.register();
|
||||
BlockStateBridges.register();
|
||||
WorldBridges.register();
|
||||
ItemSettingsBridges.register();
|
||||
EntityBridges.register();
|
||||
DamageSourceBridges.register();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
class DamageSourceBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("DamageSource.create", args -> {
|
||||
try {
|
||||
String id = ((String) args[0]).toUpperCase(Locale.ROOT);
|
||||
return DamageSource.class.getField(id).get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
Bridges.addBridge("DamageSource.createFromPlayer", args -> DamageSource.player((PlayerEntity) args[1]));
|
||||
Bridges.addBridge("DamageSource.createFromMob", args -> DamageSource.mob((LivingEntity) args[1]));
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.util.Util;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
class EntityBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("Entity.getEntityWorld", args -> ((Entity) args[0]).getEntityWorld());
|
||||
Bridges.addBridge("Entity.getID", args -> Registry.ENTITY_TYPE.getId(((Entity) args[0]).getType()).toString());
|
||||
|
||||
Bridges.addBridge("Entity.getName", args -> ((Entity) args[0]).getName().asString());
|
||||
Bridges.addBridge("Entity.getDisplayName", args -> ((Entity) args[0]).getDisplayName().asString());
|
||||
Bridges.addBridge("Entity.getCustomName", args -> ((Entity) args[0]).hasCustomName() ? Objects.requireNonNull(((Entity) args[0]).getCustomName()).asString() : null);
|
||||
|
||||
Bridges.addBridge("Entity.kill", args -> {
|
||||
((Entity) args[0]).kill();
|
||||
return null;
|
||||
});
|
||||
Bridges.addBridge("Entity.remove", args -> {
|
||||
((Entity) args[0]).remove();
|
||||
return null;
|
||||
});
|
||||
Bridges.addBridge("Entity.damage", args -> ((Entity) args[0]).damage((DamageSource) args[1], (float) args[2]));
|
||||
|
||||
Bridges.addBridge("Entity.setFireTicks", args -> {
|
||||
((Entity) args[0]).setFireTicks((int) args[1]);
|
||||
return null;
|
||||
});
|
||||
Bridges.addBridge("Entity.getFireTicks", args -> ((Entity) args[0]).getFireTicks());
|
||||
|
||||
Bridges.addBridge("Entity.getPosition", args -> {
|
||||
Vec3d pos = ((Entity) args[0]).getPos();
|
||||
Double[] out = new Double[3];
|
||||
out[0] = pos.getX();
|
||||
out[1] = pos.getY();
|
||||
out[2] = pos.getZ();
|
||||
return out;
|
||||
});
|
||||
Bridges.addBridge("Entity.setPosition", args -> {
|
||||
((Entity) args[0]).updatePosition(Util.toDouble(args[1], 0), Util.toDouble(args[2], 0), Util.toDouble(args[3], 0));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.Rarity;
|
||||
|
||||
class ItemSettingsBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("ItemSettings.create", args -> {
|
||||
Item.Settings settings = new Item.Settings();
|
||||
|
||||
settings.maxCount(((Double) args[0]).intValue());
|
||||
settings.rarity(Rarity.valueOf((String) args[1]));
|
||||
|
||||
String selectedGroup = (String) args[2];
|
||||
if (selectedGroup != null) {
|
||||
for (ItemGroup group : ItemGroup.GROUPS) {
|
||||
if (group.getName().equals(selectedGroup)) {
|
||||
settings.group(group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
class ItemStackBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("ItemStack.create", args -> new ItemStack(Registry.ITEM.get(new Identifier((String) args[0])), ((Double) args[1]).intValue()));
|
||||
Bridges.addBridge("ItemStack.getItem", args -> Registry.ITEM.getId(((ItemStack) args[0]).getItem()).toString());
|
||||
Bridges.addBridge("ItemStack.getCount", args -> (double) ((ItemStack) args[0]).getCount());
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.util.Hand;
|
||||
|
||||
class LivingEntityBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("LivingEntity.getStackInHand", args -> ((LivingEntity) args[0]).getStackInHand((Hand) args[1]));
|
||||
Bridges.addBridge("LivingEntity.getHealth", args -> ((LivingEntity) args[0]).getHealth());
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.api.CustomBlock;
|
||||
import com.thebrokenrail.scriptcraft.api.CustomItem;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
class RegistryBridge {
|
||||
static void register() {
|
||||
Bridges.addBridge("Registry.registerBlock", args -> {
|
||||
Registry.register(Registry.BLOCK, new Identifier((String) args[0]), new CustomBlock((Block.Settings) args[1], new Identifier((String) args[0])));
|
||||
return null;
|
||||
});
|
||||
Bridges.addBridge("Registry.registerItem", args -> {
|
||||
Registry.register(Registry.ITEM, new Identifier((String) args[0]), new CustomItem((Item.Settings) args[1], new Identifier((String) args[0])));
|
||||
return null;
|
||||
});
|
||||
Bridges.addBridge("Registry.registerBlockItem", args -> {
|
||||
Registry.register(Registry.ITEM, new Identifier((String) args[0]), new BlockItem(Registry.BLOCK.get(new Identifier((String) args[2])), (Item.Settings) args[1]));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.thebrokenrail.scriptcraft.bridge;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.util.Util;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
class WorldBridges {
|
||||
static void register() {
|
||||
Bridges.addBridge("World.getBlockState", args -> ((World) args[0]).getBlockState(new BlockPos((double) args[1], (double) args[2], (double) args[3])));
|
||||
Bridges.addBridge("World.setBlockState", args -> ((World) args[0]).setBlockState(new BlockPos((double) args[1], (double) args[2], (double) args[3]), (BlockState) args[4]));
|
||||
|
||||
Bridges.addBridge("World.spawnEntity", args -> {
|
||||
Entity entity = Registry.ENTITY_TYPE.get(new Identifier((String) args[4])).create((World) args[0]);
|
||||
if (entity != null) {
|
||||
entity.updatePosition(Util.toDouble(args[1], 0), Util.toDouble(args[2], 0), Util.toDouble(args[3], 0));
|
||||
((World) args[0]).spawnEntity(entity);
|
||||
return entity;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.thebrokenrail.scriptcraft.quickjs;
|
||||
|
||||
public class JSException extends Exception {
|
||||
public JSException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
147
src/main/java/com/thebrokenrail/scriptcraft/quickjs/QuickJS.java
Normal file
147
src/main/java/com/thebrokenrail/scriptcraft/quickjs/QuickJS.java
Normal file
@ -0,0 +1,147 @@
|
||||
package com.thebrokenrail.scriptcraft.quickjs;
|
||||
|
||||
import com.thebrokenrail.scriptcraft.ScriptCraft;
|
||||
import com.thebrokenrail.scriptcraft.util.OSUtil;
|
||||
import com.thebrokenrail.scriptcraft.util.ScriptCraftEntrypoint;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class QuickJS {
|
||||
public QuickJS() throws JSException {
|
||||
init();
|
||||
}
|
||||
|
||||
private long ctx;
|
||||
private long rt;
|
||||
|
||||
public native void init() throws JSException;
|
||||
|
||||
public native void free();
|
||||
|
||||
public native Object bridge(String method, Object... args) throws JSException;
|
||||
|
||||
public native void run(String data);
|
||||
|
||||
static {
|
||||
try {
|
||||
File file = File.createTempFile("lib" + ScriptCraft.NAMESPACE, OSUtil.getLibExtension());
|
||||
file.deleteOnExit();
|
||||
System.out.println("Extracting ScriptCraft Native To: " + file.getAbsoluteFile().toPath());
|
||||
InputStream so = (QuickJS.class.getResourceAsStream(File.separator + "natives" + File.separator + OSUtil.getOS() + File.separator + "lib" + ScriptCraft.NAMESPACE + OSUtil.getLibExtension()));
|
||||
if (so == null) {
|
||||
throw new RuntimeException("ScriptCraft does not support your OS: " + OSUtil.getOS());
|
||||
} else {
|
||||
Files.copy(so, file.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
System.load(file.getAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String loadModule(String name) {
|
||||
String[] arr = name.split(File.separator, 2);
|
||||
if (ScriptCraft.MOD_ID_PATTERN.matcher(arr[0]).matches()) {
|
||||
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(ScriptCraft.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 + ScriptCraft.NAMESPACE + File.separator + arr[0] + File.separator + arr[1]);
|
||||
|
||||
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"};
|
||||
|
||||
public static String normalizeModule(String baseName, String name) {
|
||||
String normalizedPath;
|
||||
|
||||
if (name.startsWith(".")) {
|
||||
Path parentPath = Paths.get(baseName.replaceAll("/", File.separator)).getParent();
|
||||
if (parentPath == null) {
|
||||
parentPath = Paths.get(File.separator);
|
||||
}
|
||||
normalizedPath = Paths.get(parentPath.toString(), name.replaceAll("/", File.separator)).normalize().toString();
|
||||
if (normalizedPath.charAt(0) == File.separatorChar) {
|
||||
normalizedPath = normalizedPath.substring(1);
|
||||
}
|
||||
} else {
|
||||
normalizedPath = name;
|
||||
}
|
||||
|
||||
String result = null;
|
||||
boolean success = false;
|
||||
|
||||
String[] arr = normalizedPath.split(File.separator, 2);
|
||||
if (ScriptCraft.MOD_ID_PATTERN.matcher(arr[0]).matches()) {
|
||||
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(ScriptCraft.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void print(String data, boolean err) {
|
||||
String[] lines = data.split("\n");
|
||||
for (String line : lines) {
|
||||
if (err) {
|
||||
System.err.println(line);
|
||||
} else {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.thebrokenrail.scriptcraft.quickjs;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class QuickJSManager {
|
||||
private static QuickJS quickjs;
|
||||
|
||||
public static abstract class Task {
|
||||
private boolean lastTask = false;
|
||||
private boolean done = false;
|
||||
private boolean err;
|
||||
private Object obj;
|
||||
|
||||
protected abstract Object run(QuickJS quickjs) throws JSException;
|
||||
}
|
||||
|
||||
private static Thread thread;
|
||||
|
||||
private static final AtomicReference<Task> quickJSInputTask = new AtomicReference<>();
|
||||
|
||||
private static final AtomicReference<Task> quickJSOutputTask = new AtomicReference<>();
|
||||
|
||||
public static Object sendTaskFromQuickJS(Task task) {
|
||||
if (Thread.currentThread() != thread) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
task.done = false;
|
||||
task.err = false;
|
||||
synchronized (lock) {
|
||||
quickJSOutputTask.set(task);
|
||||
lock.notifyAll();
|
||||
while (!task.done) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (task.err) {
|
||||
throw new RuntimeException("Java Exception While Executing Task");
|
||||
} else {
|
||||
return task.obj;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object startedLock = new Object();
|
||||
|
||||
private static class QuickJSThread extends Thread {
|
||||
private QuickJSThread() {
|
||||
super("QuickJS thread");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
init();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Task task = quickJSInputTask.get();
|
||||
if (task != null && !task.done) {
|
||||
try {
|
||||
task.obj = task.run(quickjs);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
task.obj = null;
|
||||
}
|
||||
task.done = true;
|
||||
if (task.lastTask) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
try {
|
||||
quickjs = new QuickJS();
|
||||
synchronized (startedLock) {
|
||||
startedLock.notifyAll();
|
||||
}
|
||||
} catch (JSException e) {
|
||||
started.set(false);
|
||||
synchronized (startedLock) {
|
||||
startedLock.notifyAll();
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
public static void init(Task initTask) {
|
||||
if (!started.get()) {
|
||||
started.set(true);
|
||||
|
||||
thread = new QuickJSThread();
|
||||
|
||||
synchronized (startedLock) {
|
||||
thread.start();
|
||||
try {
|
||||
startedLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!started.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendTaskToQuickJS(initTask);
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
Task task = new Task() {
|
||||
@Override
|
||||
protected Object run(QuickJS quickjs) {
|
||||
System.out.println("Freeing QuickJS");
|
||||
quickjs.free();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
task.lastTask = true;
|
||||
sendTaskToQuickJS(task);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object lock = new Object();
|
||||
|
||||
private static synchronized Object sendTaskToQuickJS(Task task) {
|
||||
if (!started.get()) {
|
||||
return null;
|
||||
}
|
||||
synchronized (lock) {
|
||||
quickJSOutputTask.set(null);
|
||||
quickJSInputTask.set(task);
|
||||
lock.notifyAll();
|
||||
if (task.lastTask) {
|
||||
return null;
|
||||
} else {
|
||||
while (true) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Task outputTask = quickJSOutputTask.get();
|
||||
if (outputTask != null && !outputTask.done) {
|
||||
try {
|
||||
outputTask.obj = outputTask.run(null);
|
||||
outputTask.err = false;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
outputTask.obj = null;
|
||||
outputTask.err = true;
|
||||
}
|
||||
outputTask.done = true;
|
||||
quickJSOutputTask.set(outputTask);
|
||||
}
|
||||
lock.notifyAll();
|
||||
if (task.done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return task.obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object bridge(String method, Object... args) {
|
||||
Task task = new Task() {
|
||||
@Override
|
||||
protected Object run(QuickJS quickjs) throws JSException {
|
||||
return quickjs.bridge(method, args);
|
||||
}
|
||||
};
|
||||
return sendTaskToQuickJS(task);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.thebrokenrail.scriptcraft.util;
|
||||
|
||||
public class MinecraftAPIEntrypoint implements ScriptCraftEntrypoint {
|
||||
@Override
|
||||
public String getModID() {
|
||||
return "minecraft";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModIndex() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldAutoLoad() {
|
||||
return false;
|
||||
}
|
||||
}
|
56
src/main/java/com/thebrokenrail/scriptcraft/util/OSUtil.java
Normal file
56
src/main/java/com/thebrokenrail/scriptcraft/util/OSUtil.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.thebrokenrail.scriptcraft.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class OSUtil {
|
||||
private static String getOSName() {
|
||||
String raw = System.getProperty("os.name").toLowerCase(Locale.ROOT);
|
||||
if (raw.contains("mac")) {
|
||||
return "macosx";
|
||||
} else if (raw.contains("linux")) {
|
||||
return "linux";
|
||||
} else if (raw.contains("windows")) {
|
||||
return "windows";
|
||||
} else {
|
||||
return "other";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getOS() {
|
||||
String raw = System.getProperty("os.arch");
|
||||
String arch;
|
||||
switch (raw) {
|
||||
case "x86":
|
||||
case "i686": {
|
||||
arch = "i686";
|
||||
break;
|
||||
}
|
||||
case "amd64": {
|
||||
arch = "x86_64";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
arch = "other";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return getOSName() + '-' + arch;
|
||||
}
|
||||
|
||||
public static String getLibExtension() {
|
||||
switch (getOSName()) {
|
||||
case "macos": {
|
||||
return ".dylib";
|
||||
}
|
||||
case "linux": {
|
||||
return ".so";
|
||||
}
|
||||
case "windows": {
|
||||
return ".dll";
|
||||
}
|
||||
default: {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.thebrokenrail.scriptcraft.util;
|
||||
|
||||
public interface ScriptCraftEntrypoint {
|
||||
String getModID();
|
||||
|
||||
String getModIndex();
|
||||
|
||||
default boolean shouldAutoLoad() {
|
||||
return true;
|
||||
}
|
||||
}
|
30
src/main/java/com/thebrokenrail/scriptcraft/util/Util.java
Normal file
30
src/main/java/com/thebrokenrail/scriptcraft/util/Util.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.thebrokenrail.scriptcraft.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@SuppressWarnings("UnnecessaryUnboxing")
|
||||
public class Util {
|
||||
public static <T extends Enum<T>> T getEnumValue(Class<T> clazz, String value, T defaultValue) {
|
||||
try {
|
||||
return Enum.valueOf(clazz, value.toUpperCase(Locale.ROOT));
|
||||
} catch (NullPointerException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static double toDouble(Object value, double defaultValue) {
|
||||
try {
|
||||
return ((Double) value).doubleValue();
|
||||
} catch (NullPointerException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean toBoolean(Object value, boolean defaultValue) {
|
||||
try {
|
||||
return ((Boolean) value).booleanValue();
|
||||
} catch (NullPointerException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
BIN
src/main/resources/assets/scriptcraft/icon.png
Normal file
BIN
src/main/resources/assets/scriptcraft/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 453 B |
32
src/main/resources/fabric.mod.json
Normal file
32
src/main/resources/fabric.mod.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "scriptcraft",
|
||||
"version": "${version}",
|
||||
"name": "${name}",
|
||||
"description": "JS API for Minecraft",
|
||||
"authors": [
|
||||
"TheBrokenRail"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://thebrokenrail.com/",
|
||||
"sources": "https://gitea.thebrokenrail.com/TheBrokenRail/ScriptCraft.git",
|
||||
"issues": "https://gitea.thebrokenrail.com/TheBrokenRail/ScriptCraft/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"icon": "assets/scriptcraft/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"com.thebrokenrail.scriptcraft.ScriptCraft"
|
||||
],
|
||||
"scriptcraft": [
|
||||
"com.thebrokenrail.scriptcraft.util.MinecraftAPIEntrypoint",
|
||||
"com.thebrokenrail.scriptcraft.Demo"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.7.4",
|
||||
"fabric": "*",
|
||||
"minecraft": "1.15.x"
|
||||
}
|
||||
}
|
8
src/main/resources/scriptcraft/.prettierrc
Normal file
8
src/main/resources/scriptcraft/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"printWidth": 2400
|
||||
}
|
158
src/main/resources/scriptcraft/minecraft/block.ts
Normal file
158
src/main/resources/scriptcraft/minecraft/block.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import { World } from './world';
|
||||
import { PlayerEntity } from './entity';
|
||||
import { useBridge, addBridge, Identifier, Hand, Pos, ActionResult, Direction, SingleRegistry } from './core';
|
||||
|
||||
/**
|
||||
* Settings for {@link CustomBlock}
|
||||
*/
|
||||
export class BlockSettings {
|
||||
readonly #material: string;
|
||||
readonly #materialColor: string;
|
||||
|
||||
#resistance: number;
|
||||
#hardness: number;
|
||||
|
||||
/**
|
||||
* Create Block Settings
|
||||
* @param material Material
|
||||
* @param materialColor Material Color
|
||||
*/
|
||||
constructor(material: string);
|
||||
constructor(material: string, materialColor: string);
|
||||
constructor(material: string, materialColor?: string) {
|
||||
this.#material = material;
|
||||
this.#materialColor = materialColor;
|
||||
|
||||
this.#resistance = 0;
|
||||
this.#hardness = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Block Strength
|
||||
* @param hardness Hardness Value
|
||||
* @param resistance Blast Resistance Value
|
||||
*/
|
||||
setStrength(hardness: number, resistance: number): BlockSettings {
|
||||
this.#hardness = hardness;
|
||||
this.#resistance = resistance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Hardness
|
||||
* @param hardness Hardness Value
|
||||
*/
|
||||
setHardness(hardness: number): BlockSettings {
|
||||
this.#hardness = hardness;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Blast Resistance
|
||||
* @param resistance Blast Resistance Value
|
||||
*/
|
||||
setResistance(resistance: number): BlockSettings {
|
||||
this.#resistance = resistance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
createJavaObject(): JavaObject {
|
||||
return useBridge('BlockSettings.create', this.#material, this.#materialColor, this.#hardness, this.#resistance) as JavaObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Block
|
||||
*/
|
||||
export abstract class CustomBlock {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
settings: BlockSettings;
|
||||
|
||||
constructor(settings: BlockSettings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called When The Block Is Used
|
||||
* @param world World
|
||||
* @param blockState Block State
|
||||
* @param pos Block Position
|
||||
* @param side Side Of The Block Used
|
||||
* @param player Player
|
||||
* @param hand Hand
|
||||
* @returns Action Result
|
||||
*/
|
||||
abstract onUse(world: World, blockState: BlockState, pos: Pos, side: Direction, player: PlayerEntity, hand: Hand): ActionResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block State
|
||||
*/
|
||||
export class BlockState {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
javaObject: JavaObject;
|
||||
|
||||
constructor(javaObject: JavaObject) {
|
||||
this.javaObject = javaObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Default Block State for Block
|
||||
* @param block Block ID
|
||||
* @returns Default Block State
|
||||
*/
|
||||
static getDefaultState(block: Identifier): BlockState {
|
||||
const obj = useBridge('BlockState.getDefaultState', block.toString()) as JavaObject;
|
||||
if (obj) {
|
||||
return new BlockState(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Block ID
|
||||
* @returns Block ID
|
||||
*/
|
||||
getBlock(): Identifier {
|
||||
const obj = useBridge('BlockState.getBlock', this.javaObject) as string;
|
||||
if (obj) {
|
||||
return new Identifier(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BlockRegistry implements SingleRegistry<CustomBlock> {
|
||||
static INSTANCE = new BlockRegistry();
|
||||
|
||||
#blocks: Map<string, CustomBlock>;
|
||||
|
||||
private constructor() {
|
||||
this.#blocks = new Map<string, CustomBlock>();
|
||||
}
|
||||
|
||||
register(id: Identifier, obj: CustomBlock) {
|
||||
this.#blocks.set(id.toString(), obj);
|
||||
useBridge('Registry.registerBlock', id.toString(), obj.settings.createJavaObject());
|
||||
}
|
||||
|
||||
get(id: Identifier): CustomBlock {
|
||||
return this.#blocks.get(id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
addBridge('CustomBlock.onUse', (id: string, world: JavaObject, state: JavaObject, x: number, y: number, z: number, side: keyof typeof Direction, player: JavaObject, hand: keyof typeof Hand): string => {
|
||||
return BlockRegistry.INSTANCE.get(new Identifier(id)).onUse(new World(world), new BlockState(state), new Pos(x, y, z), Direction[side], new PlayerEntity(player), Hand[hand]);
|
||||
});
|
236
src/main/resources/scriptcraft/minecraft/core.ts
Normal file
236
src/main/resources/scriptcraft/minecraft/core.ts
Normal file
@ -0,0 +1,236 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function addBridge(name: string, bridge: BridgeType) {
|
||||
__scriptcraft__.bridges[name] = bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function useBridge(name: string, ...args: BridgeValueType[]): BridgeValueType {
|
||||
return __scriptcraft__.useBridge(name, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action Result
|
||||
*/
|
||||
export enum ActionResult {
|
||||
PASS = 'PASS',
|
||||
SUCCESS = 'SUCCESS',
|
||||
CONSUME = 'CONSUME'
|
||||
}
|
||||
|
||||
/**
|
||||
* Hand
|
||||
*/
|
||||
export enum Hand {
|
||||
MAIN_HAND = 'MAIN_HAND',
|
||||
OFF_HAND = 'OFF_HAND'
|
||||
}
|
||||
|
||||
/**
|
||||
* Direction
|
||||
*/
|
||||
export enum Direction {
|
||||
DOWN = 'DOWN',
|
||||
UP = 'UP',
|
||||
NORTH = 'NORTH',
|
||||
SOUTH = 'SOUTH',
|
||||
WEST = 'WEST',
|
||||
EAST = 'EAST'
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility Class for {@link Direction}
|
||||
*/
|
||||
export class DirectionUtil {
|
||||
/**
|
||||
* Get Direction's Offset
|
||||
* @param direction Direction
|
||||
*/
|
||||
static getOffset(direction: Direction): Pos {
|
||||
switch (direction) {
|
||||
case Direction.UP: {
|
||||
return new Pos(0, 1, 0);
|
||||
}
|
||||
case Direction.DOWN: {
|
||||
return new Pos(0, -1, 0);
|
||||
}
|
||||
case Direction.NORTH: {
|
||||
return new Pos(0, 0, -1);
|
||||
}
|
||||
case Direction.SOUTH: {
|
||||
return new Pos(0, 0, 1);
|
||||
}
|
||||
case Direction.WEST: {
|
||||
return new Pos(-1, 0, 0);
|
||||
}
|
||||
case Direction.EAST: {
|
||||
return new Pos(1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespaced Identifier
|
||||
* "<namespace>:<path>"
|
||||
*/
|
||||
export class Identifier {
|
||||
readonly #namespace: string;
|
||||
readonly #path: string;
|
||||
|
||||
/**
|
||||
* Create Identifier
|
||||
* @param str Existing Identifier
|
||||
* @param namespace Namespace
|
||||
* @param path Path
|
||||
*/
|
||||
constructor(str: string);
|
||||
constructor(namespace: string, path: string);
|
||||
constructor(data: string, path?: string) {
|
||||
if (path) {
|
||||
this.#namespace = data;
|
||||
this.#path = path;
|
||||
} else {
|
||||
const id = data.split(':');
|
||||
this.#namespace = id[1] ? id[0] : 'minecraft';
|
||||
this.#path = id[1] ? id[1] : id[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Namespace
|
||||
*/
|
||||
getNamespace(): string {
|
||||
return this.#namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Path
|
||||
*/
|
||||
getPath(): string {
|
||||
return this.#path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert To String
|
||||
*/
|
||||
toString(): string {
|
||||
return this.#namespace + ':' + this.#path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arbitrary Position
|
||||
*/
|
||||
export class Pos {
|
||||
readonly #x: number;
|
||||
readonly #y: number;
|
||||
readonly #z: number;
|
||||
|
||||
/**
|
||||
* Create Position
|
||||
* @param x X Coordinate
|
||||
* @param y Y Coordinate
|
||||
* @param z Z Coordinate
|
||||
*/
|
||||
constructor(x: number, y: number, z: number) {
|
||||
this.#x = x;
|
||||
this.#y = y;
|
||||
this.#z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get X Coordinate
|
||||
* @returns X Coordinate
|
||||
*/
|
||||
getX(): number {
|
||||
return this.#x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Y Coordinate
|
||||
* @returns Y Coordinate
|
||||
*/
|
||||
getY(): number {
|
||||
return this.#y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Z Coordinate
|
||||
* @returns Z Coordinate
|
||||
*/
|
||||
getZ(): number {
|
||||
return this.#z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Position
|
||||
* @param pos Other Position
|
||||
* @returns Position Sum
|
||||
*/
|
||||
add(pos: Pos): Pos {
|
||||
return new Pos(this.getX() + pos.getX(), this.getY() + pos.getY(), this.getZ() + pos.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract Position
|
||||
* @param pos Other Position
|
||||
* @returns Position Difference
|
||||
*/
|
||||
subtract(pos: Pos): Pos {
|
||||
const newBlockPos = new Pos(-pos.getX(), -pos.getY(), -pos.getZ());
|
||||
return this.add(newBlockPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply Position
|
||||
* @param pos Other Position
|
||||
* @returns Position Product
|
||||
*/
|
||||
multiply(pos: Pos): Pos {
|
||||
return new Pos(this.getX() * pos.getX(), this.getY() * pos.getY(), this.getZ() * pos.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide Position
|
||||
* @param pos Position Quotient
|
||||
*/
|
||||
divide(pos: Pos): Pos {
|
||||
return new Pos(this.getX() / pos.getX(), this.getY() / pos.getY(), this.getZ() / pos.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset Position with a {@link Direction}
|
||||
* @param direction Offset Position
|
||||
*/
|
||||
offset(direction: Direction): Pos {
|
||||
return this.add(DirectionUtil.getOffset(direction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Round Position
|
||||
* @returns Rounded Position
|
||||
*/
|
||||
round(): Pos {
|
||||
return new Pos(Math.round(this.getX()), Math.round(this.getY()), Math.round(this.getZ()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert To String
|
||||
*/
|
||||
toString(): string {
|
||||
return 'Pos{' + this.getX() + ', ' + this.getY() + ', ' + this.getZ() + '}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface SingleRegistry<T> {
|
||||
register(id: Identifier, obj: T): void;
|
||||
|
||||
get(id: Identifier): T;
|
||||
}
|
233
src/main/resources/scriptcraft/minecraft/entity.ts
Normal file
233
src/main/resources/scriptcraft/minecraft/entity.ts
Normal file
@ -0,0 +1,233 @@
|
||||
import { ItemStack } from './item';
|
||||
import { useBridge, Hand, Identifier, Pos } from './core';
|
||||
import { World } from './world';
|
||||
|
||||
/**
|
||||
* Damage Source
|
||||
*/
|
||||
export class DamageSource {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
javaObject: JavaObject;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(javaObject: JavaObject) {
|
||||
this.javaObject = javaObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private static build(bridge: string, value: BridgeValueType): DamageSource {
|
||||
const obj = useBridge(bridge, value) as JavaObject;
|
||||
if (obj) {
|
||||
return new DamageSource(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Damage Source from a name
|
||||
* @param value Name
|
||||
* @returns Damage Source
|
||||
*/
|
||||
static create(value: string): DamageSource {
|
||||
return this.build('DamageSource.create', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Damage Source from a player ({@link PlayerEntity})
|
||||
* @param value Player
|
||||
* @returns Damage Source
|
||||
*/
|
||||
static createFromPlayer(value: PlayerEntity): DamageSource {
|
||||
return this.build('DamageSource.createFromPlayer', value.javaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Damage Source from a mob ({@link LivingEntity})
|
||||
* @param value Mob
|
||||
* @returns Damage Source
|
||||
*/
|
||||
static createFromMob(value: LivingEntity): DamageSource {
|
||||
return this.build('DamageSource.createFromMob', value.javaObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity
|
||||
*/
|
||||
export class Entity {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
javaObject: JavaObject;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(object: JavaObject|Entity) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
if (object instanceof Entity) {
|
||||
this.javaObject = object.javaObject;
|
||||
} else {
|
||||
this.javaObject = object;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity World
|
||||
* @returns Entity World
|
||||
*/
|
||||
getEntityWorld(): World {
|
||||
const obj = useBridge('Entity.getEntityWorld', this.javaObject) as JavaObject;
|
||||
if (obj) {
|
||||
return new World(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity ID
|
||||
* @returns Entity ID
|
||||
*/
|
||||
getID(): Identifier {
|
||||
const obj = useBridge('Entity.getID', this.javaObject) as string;
|
||||
if (obj) {
|
||||
return new Identifier(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity Name
|
||||
* @returns Entity Name
|
||||
*/
|
||||
getName(): string {
|
||||
return useBridge('Entity.getName', this.javaObject) as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity Display Name
|
||||
* @returns Entity Display Name
|
||||
*/
|
||||
getDisplayName(): string {
|
||||
return useBridge('Entity.getDisplayName', this.javaObject) as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity Custom Name
|
||||
* @returns Entity Custom Name
|
||||
*/
|
||||
getCustomName(): string {
|
||||
return useBridge('Entity.getCustomName', this.javaObject) as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert To String
|
||||
*/
|
||||
toString(): string {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill Entity
|
||||
*/
|
||||
kill() {
|
||||
useBridge('Entity.kill', this.javaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Entity
|
||||
*/
|
||||
remove() {
|
||||
useBridge('Entity.remove', this.javaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Entity Fire Ticks
|
||||
* @param ticks Fire Ticks
|
||||
*/
|
||||
setFireTicks(ticks: number) {
|
||||
useBridge('Entity.setFireTicks', this.javaObject, ticks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity Fire Ticks
|
||||
* @returns Fire Ticks
|
||||
*/
|
||||
getFireTicks(): number {
|
||||
return useBridge('Entity.getFireTicks', this.javaObject) as number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Damage Entity
|
||||
* @param source Damage Source
|
||||
* @param amount Damage Amount
|
||||
*/
|
||||
damage(source: DamageSource, amount: number) {
|
||||
useBridge('Entity.damage', source.javaObject, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Entity Position
|
||||
* @returns Entity Position
|
||||
*/
|
||||
getPosition(): Pos {
|
||||
const pos: number[] = useBridge('Entity.getPosition', this.javaObject) as number[];
|
||||
if (pos && pos.length === 3) {
|
||||
return new Pos(pos[0], pos[1], pos[2]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Entity Position
|
||||
* @param pos Entity Position
|
||||
*/
|
||||
setPosition(pos: Pos) {
|
||||
useBridge('Entity.setPosition', this.javaObject, pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Living Entity
|
||||
*/
|
||||
export class LivingEntity extends Entity {
|
||||
/**
|
||||
* Get Stack in {@link Hand}
|
||||
* @param hand Hand
|
||||
* @returns Item Stack
|
||||
*/
|
||||
getStackInHand(hand: Hand): ItemStack {
|
||||
const obj = useBridge('LivingEntity.getStackInHand', this.javaObject, hand.toString()) as JavaObject;
|
||||
if (obj) {
|
||||
return new ItemStack(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Health
|
||||
* @returns Entity Health
|
||||
*/
|
||||
getHealth(): number {
|
||||
return useBridge('LivingEntity.getHealth', this.javaObject) as number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player Entity
|
||||
*/
|
||||
export class PlayerEntity extends LivingEntity {
|
||||
}
|
6
src/main/resources/scriptcraft/minecraft/index.ts
Normal file
6
src/main/resources/scriptcraft/minecraft/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { Identifier, ActionResult, Hand, Pos, Direction, DirectionUtil } from './core';
|
||||
export { CustomBlock, BlockSettings, BlockState } from './block';
|
||||
export { ItemStack, ItemSettings, CustomItem, BlockItem } from './item';
|
||||
export { World } from './world';
|
||||
export { LivingEntity, PlayerEntity } from './entity';
|
||||
export { Registry } from './registry';
|
221
src/main/resources/scriptcraft/minecraft/item.ts
Normal file
221
src/main/resources/scriptcraft/minecraft/item.ts
Normal file
@ -0,0 +1,221 @@
|
||||
import { useBridge, Identifier, Hand, ActionResult, addBridge, Pos, Direction, SingleRegistry } from './core';
|
||||
import { World } from './world';
|
||||
import { PlayerEntity, LivingEntity } from './entity';
|
||||
|
||||
/**
|
||||
* Item Stack
|
||||
*/
|
||||
export class ItemStack {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
javaObject: JavaObject;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(obj: JavaObject) {
|
||||
this.javaObject = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Item Stack
|
||||
* @param item Item ID
|
||||
* @param count Item Count
|
||||
*/
|
||||
static create(item: Identifier, count?: number): ItemStack {
|
||||
const obj = useBridge('ItemStack.create', item.toString(), count ? count : 1) as JavaObject;
|
||||
if (obj) {
|
||||
return new ItemStack(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Item ID
|
||||
* @returns Item ID
|
||||
*/
|
||||
getItem(): Identifier {
|
||||
const obj = useBridge('ItemStack.getItem', this.javaObject) as string;
|
||||
if (obj) {
|
||||
return new Identifier(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Item Count
|
||||
* @returns Item Count
|
||||
*/
|
||||
getCount(): number {
|
||||
return useBridge('ItemStack.getCount', this.javaObject) as number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Item Rarity
|
||||
*/
|
||||
enum ItemRarity {
|
||||
COMMON = 'COMMON',
|
||||
UNCOMMON = 'UNCOMMON',
|
||||
RARE = 'RARE',
|
||||
EPIC = 'EPIC'
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings for {@link CustomItem} and {@link BlockItem}
|
||||
*/
|
||||
export class ItemSettings {
|
||||
#maxCount: number;
|
||||
#itemGroup: string;
|
||||
#rarity: ItemRarity;
|
||||
|
||||
/**
|
||||
* Create Item Settings
|
||||
*/
|
||||
constructor() {
|
||||
this.#maxCount = 64;
|
||||
this.#rarity = ItemRarity.COMMON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Max Count
|
||||
* @param maxCount Max Count
|
||||
*/
|
||||
setMaxCount(maxCount: number): ItemSettings {
|
||||
this.#maxCount = maxCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Item Group
|
||||
* @param itemGroup Item Group
|
||||
*/
|
||||
setItemGroup(itemGroup: string | Identifier): ItemSettings {
|
||||
this.#itemGroup = itemGroup.toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Item Rarity
|
||||
* @param rarity Item Rarity
|
||||
*/
|
||||
setRarity(rarity: ItemRarity): ItemSettings {
|
||||
this.#rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
createJavaObject(): JavaObject {
|
||||
return useBridge('ItemSettings.create', this.#maxCount, this.#rarity, this.#itemGroup) as JavaObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Item
|
||||
*/
|
||||
export abstract class CustomItem {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
settings: ItemSettings;
|
||||
|
||||
constructor(settings: ItemSettings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called When The Item Is Used
|
||||
* @param world World
|
||||
* @param player Player
|
||||
* @param hand Hand
|
||||
* @returns Action Result
|
||||
*/
|
||||
abstract onUse(world: World, player: PlayerEntity, hand: Hand): ActionResult;
|
||||
|
||||
/**
|
||||
* Called When The Item Is Used On A Block
|
||||
* @param world World
|
||||
* @param pos Block Position
|
||||
* @param side Side Of The Block Used
|
||||
* @param player Player
|
||||
* @param hand Hand
|
||||
* @returns Action Result
|
||||
*/
|
||||
abstract onUseOnBlock(world: World, pos: Pos, side: Direction, player: PlayerEntity, hand: Hand): ActionResult;
|
||||
|
||||
/**
|
||||
* Called When The Item Is Used On An Entity
|
||||
* @param player Player
|
||||
* @param target Target Entity
|
||||
* @param hand Hand
|
||||
* @returns Action Result
|
||||
*/
|
||||
abstract onUseOnEntity(player: PlayerEntity, target: LivingEntity, hand: Hand): ActionResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Item
|
||||
*/
|
||||
export class BlockItem {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
settings: ItemSettings;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
block: Identifier;
|
||||
|
||||
/**
|
||||
* Create Block Item
|
||||
* @param block Block ID
|
||||
* @param settings Item Settings
|
||||
*/
|
||||
constructor(block: Identifier, settings: ItemSettings) {
|
||||
this.settings = settings;
|
||||
this.block = block;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class ItemRegistry implements SingleRegistry<CustomItem | BlockItem> {
|
||||
static INSTANCE = new ItemRegistry();
|
||||
|
||||
#items: Map<string, CustomItem>;
|
||||
|
||||
private constructor() {
|
||||
this.#items = new Map<string, CustomItem>();
|
||||
}
|
||||
|
||||
register(id: Identifier, obj: CustomItem | BlockItem) {
|
||||
if (obj instanceof CustomItem) {
|
||||
this.#items.set(id.toString(), obj);
|
||||
useBridge('Registry.registerItem', id.toString(), obj.settings.createJavaObject());
|
||||
} else {
|
||||
useBridge('Registry.registerBlockItem', id.toString(), obj.settings.createJavaObject(), obj.block.toString());
|
||||
}
|
||||
}
|
||||
|
||||
get(id: Identifier): CustomItem {
|
||||
return this.#items.get(id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
addBridge('CustomItem.onUse', (id: string, world: JavaObject, player: JavaObject, hand: keyof typeof Hand): string => {
|
||||
return ItemRegistry.INSTANCE.get(new Identifier(id)).onUse(new World(world), new PlayerEntity(player), Hand[hand]);
|
||||
});
|
||||
|
||||
addBridge('CustomItem.onUseOnBlock', (id: string, world: JavaObject, x: number, y: number, z: number, side: keyof typeof Direction, player: JavaObject, hand: keyof typeof Hand): string => {
|
||||
return ItemRegistry.INSTANCE.get(new Identifier(id)).onUseOnBlock(new World(world), new Pos(x, y, z), Direction[side], new PlayerEntity(player), Hand[hand]);
|
||||
});
|
||||
|
||||
addBridge('CustomItem.onUseOnEntity', (id: string, player: JavaObject, target: JavaObject, hand: keyof typeof Hand): boolean => {
|
||||
return ItemRegistry.INSTANCE.get(new Identifier(id)).onUseOnEntity(new PlayerEntity(player), new LivingEntity(target), Hand[hand]) === ActionResult.SUCCESS ? true : false;
|
||||
});
|
37
src/main/resources/scriptcraft/minecraft/registry.ts
Normal file
37
src/main/resources/scriptcraft/minecraft/registry.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { Identifier, SingleRegistry } from './core';
|
||||
import { BlockRegistry } from './block';
|
||||
import { ItemRegistry } from './item';
|
||||
|
||||
/**
|
||||
* Registry
|
||||
*/
|
||||
export abstract class Registry {
|
||||
/**
|
||||
* Block Registry
|
||||
*/
|
||||
static BLOCK = BlockRegistry.INSTANCE;
|
||||
/**
|
||||
* Item Registry
|
||||
*/
|
||||
static ITEM = ItemRegistry.INSTANCE;
|
||||
|
||||
/**
|
||||
* Register Object
|
||||
* @param registry Target Registry
|
||||
* @param id ID
|
||||
* @param obj Object
|
||||
*/
|
||||
static register<T>(registry: SingleRegistry<T>, id: Identifier, obj: T): T {
|
||||
registry.register(id, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Object From Registry
|
||||
* @param registry Target Registry
|
||||
* @param id ID
|
||||
*/
|
||||
static get<T>(registry: SingleRegistry<T>, id: Identifier): T {
|
||||
return registry.get(id);
|
||||
}
|
||||
}
|
58
src/main/resources/scriptcraft/minecraft/world.ts
Normal file
58
src/main/resources/scriptcraft/minecraft/world.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { BlockState } from './block';
|
||||
import { Entity } from './entity';
|
||||
import { useBridge, Pos, Identifier } from './core';
|
||||
|
||||
/**
|
||||
* World
|
||||
*/
|
||||
export class World {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
javaObject: JavaObject;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(javaObject: JavaObject) {
|
||||
this.javaObject = javaObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Block State
|
||||
* @param pos Position
|
||||
* @param state Block State
|
||||
* @returns True If Successful
|
||||
*/
|
||||
setBlockState(pos: Pos, state: BlockState): boolean {
|
||||
return useBridge('World.setBlockState', this.javaObject, pos.getX(), pos.getY(), pos.getZ(), state.javaObject) as boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Block State
|
||||
* @param pos Position
|
||||
* @returns Block State
|
||||
*/
|
||||
getBlockState(pos: Pos): BlockState {
|
||||
const obj = useBridge('World.getBlockState', this.javaObject, pos.getX(), pos.getY(), pos.getZ()) as JavaObject;
|
||||
if (obj) {
|
||||
return new BlockState(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn Entity
|
||||
* @param id Entity ID
|
||||
* @param pos Position
|
||||
*/
|
||||
spawnEntity(id: Identifier, pos: Pos): Entity {
|
||||
const obj = useBridge('World.spawnEntity', this.javaObject, pos.getX(), pos.getY(), pos.getZ(), id.toString()) as JavaObject;
|
||||
if (obj) {
|
||||
return new Entity(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
41
src/main/resources/scriptcraft/test/index.ts
Normal file
41
src/main/resources/scriptcraft/test/index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { CustomBlock, BlockSettings, Identifier, Registry, BlockState, ActionResult, World, Pos, Hand, PlayerEntity, BlockItem, ItemSettings, CustomItem, Direction, LivingEntity } from 'minecraft';
|
||||
|
||||
console.log('hello');
|
||||
|
||||
class MyBlock extends CustomBlock {
|
||||
constructor() {
|
||||
super(new BlockSettings('STONE', 'IRON'));
|
||||
}
|
||||
|
||||
onUse(world: World, blockState: BlockState, blockPos: Pos, side: Direction, player: PlayerEntity, hand: Hand): ActionResult {
|
||||
world.setBlockState(blockPos.offset(side), BlockState.getDefaultState(new Identifier('minecraft:stone')));
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Registry.register(Registry.BLOCK, new Identifier('test', 'my_block'), new MyBlock());
|
||||
Registry.register(Registry.ITEM, new Identifier('test', 'my_block'), new BlockItem(new Identifier('test', 'my_block'), new ItemSettings()));
|
||||
|
||||
class MyItem extends CustomItem {
|
||||
constructor() {
|
||||
super(new ItemSettings().setItemGroup('building_blocks').setMaxCount(128));
|
||||
}
|
||||
|
||||
onUse(world: World, player: PlayerEntity, hand: Hand): ActionResult {
|
||||
console.log('Item Use: Normal');
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
onUseOnBlock(world: World, blockPos: Pos, side: Direction, player: PlayerEntity, hand: Hand): ActionResult {
|
||||
console.log('Item Use: Block ' + blockPos.toString());
|
||||
world.spawnEntity(new Identifier('minecraft:cow'), blockPos.offset(side).add(new Pos(0.5, 0, 0.5))).toString();
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
onUseOnEntity(player: PlayerEntity, target: LivingEntity, hand: Hand): ActionResult {
|
||||
console.log('Item Use: Entity ' + target.getPosition());
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Registry.register(Registry.ITEM, new Identifier('test', 'my_item'), new MyItem());
|
20
src/main/resources/scriptcraft/tsconfig.json
Normal file
20
src/main/resources/scriptcraft/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"lib": ["es2020"],
|
||||
"module": "es2020",
|
||||
"target": "es2020",
|
||||
"typeRoots": ["types"],
|
||||
"rootDir": ".",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"minecraft": ["minecraft/index"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
7
src/main/resources/scriptcraft/typedoc.json
Normal file
7
src/main/resources/scriptcraft/typedoc.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"mode": "modules",
|
||||
"readme": "none",
|
||||
"excludeNotExported": true,
|
||||
"excludePrivate": true,
|
||||
"stripInternal": true
|
||||
}
|
28
src/main/resources/scriptcraft/types/scriptcraft/index.d.ts
vendored
Normal file
28
src/main/resources/scriptcraft/types/scriptcraft/index.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
interface JavaObject {
|
||||
discriminator: 'JavaObject';
|
||||
}
|
||||
|
||||
type BridgeValueType = string | number | boolean | JavaObject | BridgeValueType[];
|
||||
type BridgeType = (...args: BridgeValueType[]) => BridgeValueType;
|
||||
|
||||
interface ScriptCraft {
|
||||
useBridge(name: string, ...args: BridgeValueType[]): BridgeValueType;
|
||||
|
||||
bridges: { [key: string]: BridgeType };
|
||||
}
|
||||
|
||||
declare const __scriptcraft__: ScriptCraft;
|
||||
|
||||
interface Console {
|
||||
log(...args: string[]): void;
|
||||
info(...args: string[]): void;
|
||||
warn(...args: string[]): void;
|
||||
error(...args: string[]): void;
|
||||
dir(...args: string[]): void;
|
||||
debug(...args: string[]): void;
|
||||
trace(...args: string[]): void;
|
||||
|
||||
assert(condition: boolean, ...args: string[]): void;
|
||||
}
|
||||
|
||||
declare const console: Console;
|
Reference in New Issue
Block a user