Joban Wiki Difference between revisions of "JCM:Scripting"

Difference between revisions of "JCM:Scripting"

From Joban Wiki
(Created page with "==Scripting Documentation== JCM Scripting is a feature introduced in '''JCM v2.0.0-beta.5'''. It serves as a testbed for scripting in MTR 4, as well as using JavaScript to control PIDS. The system itself is heavily influenced by the Nemo Transit Expansion and shares much of the similarities. As such, most of the documentation include this page is also referenced from the [https://wiki.minecrafttransitrailway.com/mtr_addon:nte:js:start NTE JS documentation]. Currently t...")
 
 
(7 intermediate revisions by the same user not shown)
Line 4: Line 4:
The system itself is heavily influenced by the Nemo Transit Expansion and shares much of the similarities. As such, most of the documentation include this page is also referenced from the [https://wiki.minecrafttransitrailway.com/mtr_addon:nte:js:start NTE JS documentation].
The system itself is heavily influenced by the Nemo Transit Expansion and shares much of the similarities. As such, most of the documentation include this page is also referenced from the [https://wiki.minecrafttransitrailway.com/mtr_addon:nte:js:start NTE JS documentation].


Currently the only supported type of scripting is '''PIDS Scripting''', check [[JCM:Scripting:Documentation:PIDS|Scripted PIDS Documentation]] for more detail.
''Note: This page serves as a documentation for the whole scripting system implemented in JCM, which includes Eyecandy/Decoration Block Scripting and [[JCM:Scripting:Documentation:PIDS|PIDS Scripting]].''


===<nowiki>===== Subpages ====</nowiki>===
===<nowiki>===== Subpages ====</nowiki>===
Line 12: Line 12:
*[[JCM:Scripting:Documentation:Dynamic Textures|Dynamic Textures]]
*[[JCM:Scripting:Documentation:Dynamic Textures|Dynamic Textures]]
*[[JCM:Scripting:Documentation:PIDS|Scripted PIDS Documentation]]
*[[JCM:Scripting:Documentation:PIDS|Scripted PIDS Documentation]]
*[https://wiki.minecrafttransitrailway.com/mtr_addon:nte:js:eyecandy Scripted Eyecandy Documentation (Same as NTE)]


==Introduction==
==Introduction==


===How To Edit JavaScript Files?===
=== What is JavaScript? ===
A JavaScript file (.js) can be edited in a variety of editors. It is recommended to have a text editor that supports JavaScript Syntax Highlighting like Notepad++ or Sublime text. While a full IDE (like Visual Studio Code or IntelliJ) is not recommended, it can also be used as a basic text editor.
JavaScript is a programming language that... in very simple terms, instructs computer to do stuff :D


This rest of this article assumes that you have a basic understanding of JavaScript and JavaScript types, so it won't delve into the basic syntax and other aspects of it here. You can learn JavaScript from resources on the web, such as here.
It can describe logic, an example would be:


===Type Annotation===
''<u>If</u> there's pineapple on top of the pizza, <u>then</u> remove the pineapple and eat the pizza, <u>otherwise</u> eat the pizza.''
 
This rest of this article assumes that you have a basic understanding of JavaScript and JavaScript types, so it won't delve into the basic syntax and other aspects of it here. You can learn JavaScript from resources on the web, such as [here]. // TODO: I need the link
 
=== The Nature of Scripting in JCM ===
While JavaScript is commonly associated with webpages or even server applications (via Node.js), JCM's implementation of JavaScript only utilize the language itself (Along with a couple other objects such as [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date Date]).
 
As such, this means that you only really need to care about the language itself (e.g. Variable & Function Declaration, conditional logic). Other stuff such as HTML/CSS/DOM manipulation <u>does not apply</u> to JCM Scripting.
 
Keep that in mind, as IDE (Such as Visual Studio Code) may assume you are developing for a webpage and provides suggestions that are not applicable to JCM/NTE scripting!
 
=== Do I have to learn Java to write JavaScript? ===
JavaScript does not have anything, or not much to do with Java at all, even though they share "Java" in the name.
 
=== But can I use Java in JavaScript? ===
Under normal circumstances, no.
 
''However'' the JavaScript Engine that JCM/NTE uses, '''Rhino''', ''do'' allow using classes from the standard Java library as <code>java.package.name</code>. Using classes from MTR is not available yet, most likely due to problems with the class loader.
 
=== Script Flow ===
An example flow is available below. This chart assumes the player is running Minecraft at 13fps (For simplicity sake), which means 13 frames in 1 second.[[File:JCM Script Execution Example.png|559x559px]]
 
Immediately you may have noticed the following thing:
 
==== Scripts are executed asynchronously ====
This means that the script runs in the background and does not prevent the game from continue rendering (Therefore, less fps lag).
 
'''Note that this does NOT mean you can freely block script execution or run some Thread.Sleep, as you would be blocking the script thread, making others (and your) script run slower!'''
 
==== Scripts are executed every frame ====
More precisely, the <code>render()</code> function is executed every frame.
 
If there's a lag spike (Seen in '''Frame 8'''), your script would be not be called until the next frame came around, which is 154ms later in the above example.
 
As such, you should not assume that your function will always be called "x times per second", or "xx ms after the last one".
 
This also means that if you increment a variable by a fixed amount for each frame, that increment speed won't be the same if the fps is higher/lower.
 
[[wikipedia:Delta_timing|Delta timing]] is used to solve this by obtaining the time since last frame, which can then be used to balance out the value.
 
==== Scripts are NOT always executed every frame! ====
While JCM ''tries'' to call the <code>render()</code> function for every frame, it is only made on a best-effort basis. If your script has not finished executing before the next frame came around, then your function won't be called again until it has finished execution.
 
===Errors===
If the script is executed incorrectly, an error will be reported in the Minecraft log (Starting with “'''[Scripting] Error executing script!'''”). '''(Please note that at the moment there will not be any in-game indication''')
 
The error message will indicate which line of code in which script file the error occurred. Most launchers have the ability to display logs in a separate window in real time.
 
The script execution engine will then pause the entire script for 4 seconds before executing the function again.
 
=== Documentation Format ===
As you know, values ​​in JS have different types. When calling a function, you must pass parameters of the appropriate type, and the result it returns will also have a type. In this article, all of the functions have their parameter and return types stated. For example:
As you know, values ​​in JS have different types. When calling a function, you must pass parameters of the appropriate type, and the result it returns will also have a type. In this article, all of the functions have their parameter and return types stated. For example:


Line 38: Line 89:
* <code>: void</code> means that the function has no return value.
* <code>: void</code> means that the function has no return value.


===Using Built-In Java Classes===
=== Tips & Notes ===
The Rhino JS engine allows using classes from the standard Java library as <code>java.package.name</code>. Using classes from MTR is not available yet, most likely due to problems with the class loader.


===Declaring variables using let or var===
====Declaring variables using let or var====
Both NTE and JCM uses JavaScript's strict mode, which does not allow variables to be assigned immediately without declaring them. To declare a global variable, use syntax like <code>var glb;</code> or outside a function <code>var glb = 1;</code>. To use local variables inside a function, use <code>let local;</code> or <code>let local = 1;</code>.
Both JCM/NTE uses JavaScript's '''strict mode''', which does not allow variables to be assigned without first declaring them.
 
This means you can't do <code>local = 1</code> and expect <code>local</code> to be automatically defined.
 
Instead, you have use syntax like <code>var glb;</code> or outside a function <code>var glb = 1;</code>. For local variables inside a function, use <code>let local;</code> or <code>let local = 1;</code>.


[Translator's note: in general, it is better to always try to use <code>let</code> and resort to using <code>var</code> as a last resort].
[Translator's note: in general, it is better to always try to use <code>let</code> and resort to using <code>var</code> as a last resort].


===Don't Block Or Infinitely Loop===
====Don't Block Or Infinitely Loop====
JCM calls the function you wrote once per frame and expects your function to finish processing and return a value as soon as possible. Therefore, there is no such thing as “stop execution and wait for a while”. If you want to achieve this behavior, you need to time it and then execute the appropriate action on a call made at the right time.
The function you wrote are called once per frame by JCM, where  your function is expected to finish processing and return a value ''as soon/fast as possible''. As such, there's no such concept as “'''wait for a while before continue executing'''”.
 
Instead, what you likely want is to "do a thing later on", in such case you will need to time it and then execute the appropriate action on a call made at the right time.
 
If you are trying to execute a long-running operation (e.g. Fetching data over the internet), you should submit it to another thread/executor.


If locks or infinite loops occur in the code, the entire script execution will stall because JCM scripts are executed one by one at a time [in the same thread]. This can be reset with F3+T.
If blocks or infinite loops did occur, then the entire script execution will stall as scripts are executed one at a time [''in the same thread'']. In such situation, you can reset it with F3+T.


=== Differences In Using Java Classes ===
==== Interoperability between Java Classes/Methods ====
For common function types such as strings, Java and JavaScript have different class implementations, which causes there to be JavaScript strings as well as Java strings. NTE/MTR functions and fields return Java string classes rather than JavaScript strings, but Rhino does some conversions automatically, so in most cases you can mix and match, but sometimes this can cause problems.
For common function types such as strings, Java and JavaScript have different class implementations, which causes there to be JavaScript strings as well as Java strings.
 
NTE/MTR functions and fields return Java string classes rather than JavaScript strings, but Rhino does some conversions automatically, so in most cases you can mix and match, but sometimes this can cause problems.


For example, here's an example of a problem caused by using <code>str.length()</code> from a Java string class and <code>str.length</code> from a JavaScript string class to get the length of a string:<pre>
For example, here's an example of a problem caused by using <code>str.length()</code> from a Java string class and <code>str.length</code> from a JavaScript string class to get the length of a string:<pre>
Line 61: Line 121:
</pre>Similarly, there is a <code>List<T></code> type in Java. It does the same thing as arrays in JavaScript, but has a different type. Trying to call JavaScript array functions on it won't work, but Rhino adapts it so that you can take values with <code>list[0]</code> or loop through them with <code>for (a of list)</code>.
</pre>Similarly, there is a <code>List<T></code> type in Java. It does the same thing as arrays in JavaScript, but has a different type. Trying to call JavaScript array functions on it won't work, but Rhino adapts it so that you can take values with <code>list[0]</code> or loop through them with <code>for (a of list)</code>.


===Supported Parts Of The JavaScript Standard===
====What JavaScript features are supported?====
The Rhino engine does not support all of the latest JavaScript features. See [https://mozilla.github.io/rhino/compat/engines.html Mozilla's documentation] for details on what is supported. JCM uses Rhino 1.7.15 with the <code>VERSION_ES6</code> flag enabled.
The Rhino JavaScript Engine '''does not support''' all of the latest JavaScript features. See [https://mozilla.github.io/rhino/compat/engines.html Mozilla's documentation] for details on what is supported. JCM uses Rhino 1.7.15 with the <code>VERSION_ES6</code> flag enabled.
 
===Errors===
If the script is executed incorrectly, JCM will report an error in the Minecraft log(Starting with “'''[JCM] [Scripting] Error executing script!'''”), but please note that there will be no in-game indication. The error message will indicate which line of code in which script file the error occurred. Most launchers have the ability to display logs in a separate window in real time.
 
JCM will then pause the entire script for 4 seconds before executing the function again.


=== Running Other Scripts ===
=== Including Other Scripts ===
By calling the function below, you can include and execute other JavaScript files.
{| class="wikitable"
{| class="wikitable"
|+
|+

Latest revision as of 00:38, 24 February 2025

Scripting Documentation

JCM Scripting is a feature introduced in JCM v2.0.0-beta.5. It serves as a testbed for scripting in MTR 4, as well as using JavaScript to control PIDS.

The system itself is heavily influenced by the Nemo Transit Expansion and shares much of the similarities. As such, most of the documentation include this page is also referenced from the NTE JS documentation.

Note: This page serves as a documentation for the whole scripting system implemented in JCM, which includes Eyecandy/Decoration Block Scripting and PIDS Scripting.

===== Subpages ====

Introduction

What is JavaScript?

JavaScript is a programming language that... in very simple terms, instructs computer to do stuff :D

It can describe logic, an example would be:

If there's pineapple on top of the pizza, then remove the pineapple and eat the pizza, otherwise eat the pizza.

This rest of this article assumes that you have a basic understanding of JavaScript and JavaScript types, so it won't delve into the basic syntax and other aspects of it here. You can learn JavaScript from resources on the web, such as [here]. // TODO: I need the link

The Nature of Scripting in JCM

While JavaScript is commonly associated with webpages or even server applications (via Node.js), JCM's implementation of JavaScript only utilize the language itself (Along with a couple other objects such as Date).

As such, this means that you only really need to care about the language itself (e.g. Variable & Function Declaration, conditional logic). Other stuff such as HTML/CSS/DOM manipulation does not apply to JCM Scripting.

Keep that in mind, as IDE (Such as Visual Studio Code) may assume you are developing for a webpage and provides suggestions that are not applicable to JCM/NTE scripting!

Do I have to learn Java to write JavaScript?

JavaScript does not have anything, or not much to do with Java at all, even though they share "Java" in the name.

But can I use Java in JavaScript?

Under normal circumstances, no.

However the JavaScript Engine that JCM/NTE uses, Rhino, do allow using classes from the standard Java library as java.package.name. Using classes from MTR is not available yet, most likely due to problems with the class loader.

Script Flow

An example flow is available below. This chart assumes the player is running Minecraft at 13fps (For simplicity sake), which means 13 frames in 1 second.JCM Script Execution Example.png

Immediately you may have noticed the following thing:

Scripts are executed asynchronously

This means that the script runs in the background and does not prevent the game from continue rendering (Therefore, less fps lag).

Note that this does NOT mean you can freely block script execution or run some Thread.Sleep, as you would be blocking the script thread, making others (and your) script run slower!

Scripts are executed every frame

More precisely, the render() function is executed every frame.

If there's a lag spike (Seen in Frame 8), your script would be not be called until the next frame came around, which is 154ms later in the above example.

As such, you should not assume that your function will always be called "x times per second", or "xx ms after the last one".

This also means that if you increment a variable by a fixed amount for each frame, that increment speed won't be the same if the fps is higher/lower.

Delta timing is used to solve this by obtaining the time since last frame, which can then be used to balance out the value.

Scripts are NOT always executed every frame!

While JCM tries to call the render() function for every frame, it is only made on a best-effort basis. If your script has not finished executing before the next frame came around, then your function won't be called again until it has finished execution.

Errors

If the script is executed incorrectly, an error will be reported in the Minecraft log (Starting with “[Scripting] Error executing script!”). (Please note that at the moment there will not be any in-game indication)

The error message will indicate which line of code in which script file the error occurred. Most launchers have the ability to display logs in a separate window in real time.

The script execution engine will then pause the entire script for 4 seconds before executing the function again.

Documentation Format

As you know, values ​​in JS have different types. When calling a function, you must pass parameters of the appropriate type, and the result it returns will also have a type. In this article, all of the functions have their parameter and return types stated. For example:

static Resources.id(idStr: string): Identifier
  • static means that you don't need to create an object to use this function, you can call Resources.id("aaa:bbb") directly.
  • idStr: string means that the idStr parameter accepts a string.
  • : Identifier means that a function call will return a value of type Identifier.
Matrices.rotateX(radian: float): void
  • The lack of static means that an object is required to execute the function. For example, if a is an object of Matrices type, then the function can be called as a.rotateX(Math.PI).
  • radian: float means that the parameter takes a numeric argument. Although JS does not distinguish between integers and fractional numbers, this article will specify a specific type - int, long, float or double - to make it clear whether a parameter can accept decimal parts and to what precision.
  • : void means that the function has no return value.

Tips & Notes

Declaring variables using let or var

Both JCM/NTE uses JavaScript's strict mode, which does not allow variables to be assigned without first declaring them.

This means you can't do local = 1 and expect local to be automatically defined.

Instead, you have use syntax like var glb; or outside a function var glb = 1;. For local variables inside a function, use let local; or let local = 1;.

[Translator's note: in general, it is better to always try to use let and resort to using var as a last resort].

Don't Block Or Infinitely Loop

The function you wrote are called once per frame by JCM, where your function is expected to finish processing and return a value as soon/fast as possible. As such, there's no such concept as “wait for a while before continue executing”.

Instead, what you likely want is to "do a thing later on", in such case you will need to time it and then execute the appropriate action on a call made at the right time.

If you are trying to execute a long-running operation (e.g. Fetching data over the internet), you should submit it to another thread/executor.

If blocks or infinite loops did occur, then the entire script execution will stall as scripts are executed one at a time [in the same thread]. In such situation, you can reset it with F3+T.

Interoperability between Java Classes/Methods

For common function types such as strings, Java and JavaScript have different class implementations, which causes there to be JavaScript strings as well as Java strings.

NTE/MTR functions and fields return Java string classes rather than JavaScript strings, but Rhino does some conversions automatically, so in most cases you can mix and match, but sometimes this can cause problems.

For example, here's an example of a problem caused by using str.length() from a Java string class and str.length from a JavaScript string class to get the length of a string:

var stationName = train.getThisRoutePlatforms().get(0).station.name;
print(stationName.length); // Error: stationName is a Java string, not a JavaScript string
print(stationName.length()); // Java strings get their length from the length() function, not the JavaScript length field
print((""+stationName).length) // Use ""+ to turn it into a JavaScript string.

Similarly, there is a List<T> type in Java. It does the same thing as arrays in JavaScript, but has a different type. Trying to call JavaScript array functions on it won't work, but Rhino adapts it so that you can take values with list[0] or loop through them with for (a of list).

What JavaScript features are supported?

The Rhino JavaScript Engine does not support all of the latest JavaScript features. See Mozilla's documentation for details on what is supported. JCM uses Rhino 1.7.15 with the VERSION_ES6 flag enabled.

Including Other Scripts

By calling the function below, you can include and execute other JavaScript files.

Functions Description
static include(relPath: String): void Loads and runs another JS file relative to the current JS file.
static include(path: Identifier): void Loads and runs the JS file by location in the resource pack.

For example, include(Resources.id(“mtr:path/absolute.js”)).

Source