Skip to main content

Starting A New JavaScript Virtual Machine

Starting your own virtual machine is essential for executing traditional JavaScript code within puerts.

Example use cases include:

  • Executing a one-time script (e.g Printing 'Hello World!')
  • Starting an entry point for a complex event loop (Similar to a main function in C)

Table Of Contents​

Usage​

To start a new JavaScript virtual environment, a suitable entry point should be identified.

Example 1 - One-Time Script​

C++​
#include "JsEnv.h"

UCLASS()
class APlayerCharacter : public ACharacter
{
GENERATED_BODY()

public:
virtual void BeginPlay() override {
auto JsEnv = MakeShared<puerts::FJsEnv>();

JsEnv->Start("PrintHelloWorld.js");
}
};
TypeScript​
// YourProject/TypeScript/PrintHelloWorld.ts

console.warn("Hello World!");

Example 2 - Example Event Loop Entry Point​

C++​
#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "JsEnv.h"
#include "DemoGameInstance.generated.h"

UCLASS()
class UDemoGameInstance : public UGameInstance
{
GENERATED_BODY()

public:
virtual void OnStart() override {
Super::OnStart();
JsEnv = MakeShared<puerts::FJsEnv>();

JsEnv->Start("Entry.js",
{
TPair<FString, UObject*>("GameInstance", this)
});
}

virtual void Shutdown() override {
Super::Shutdown();
JsEnv.Reset();
}

protected:
TSharedPtr<puerts::FJsEnv> JsEnv;
};
TypeScript​
// YourProject/TypeScript/Entry.ts

import * as UE from 'ue'
import { argv } from "puerts";

const GameInstance = argv.getByName("GameInstance") as UE.GameInstance;

console.warn("JavaScript Entry Point Started!");
//...

Note: If multiple virtual machines are started, these virtual machines are isolated from each other and do not share the same memory scope.

API Reference​

Table Of Contents​

JavaScript Environment​

When starting a new JavaScript virtual machine, an instance of FJsEnv is the main object responsible for looking after the whole environment (including memory).

It contains three noteworthy functions: | Name | Arguments | Return | Description | | :---: | :---: | :---: | --- | | Start | FString, TArray<TPair<FString, UObject*> | void | Starts a new JavaScript virtual machine and executes the specified module / script, passing through any specified parameters into 'argv' | | WaitDebugger | double | void | Halts the main thread to wait for a debugger attachment. Times out after some specified milliseconds | | CurrentStackTrace | void | FString | Returns the current stack trace as a string |

When passing in parameters to the FJsEnv, there are a couple of things to note:

  • Parameters must be instances of UObject (or a derivative)
  • It works like a map. Specify the name of the parameter and assign it a value

Parameters can then be accessed from anywhere inside of the JavaScript environment through argv. (Including imported modules)

C++​
JsEnv->Start("ArgvExample.js", 
{
TPair<FString, UObject*>("World", GetWorld()),
//...
});
TypeScript​
// YourProject/TypeScript/ArgvExample.ts

import * as UE from 'ue'
import { argv } from "puerts";

const World = argv.getByName("World") as UE.World;

if (World)
console.warn("World passed in as a parameter!");

Debugging (More Info)​

Upon creating an instance of FJSEnv, there are a couple of extra parameters we can specify to start the virtual machine in debug mode.

C++​
auto JsEnv = MakeShared<puerts::FJsEnv>(
std::make_unique<puerts::DefaultJSModuleLoader>(TEXT("JavaScript")), // Specifies the default script location (Default: "Content/JavaScript")
std::make_shared<puerts::FDefaultLogger>(), // A logger object to catch console.log
1337 // The port to start the debugger on (i.e. Attach to this with VSCode)
);

// Optionally wait for the debugger to attach
JsEnv->WaitDebugger(1);

// Start the JavaScript virtual environment
JsEnv->Start(/*...*/);

Note: If you do not wait for the debugger, breakpoints will be skipped prior to attachment.

Automatic Binding Mode​

Now that you know how to start your own JavaScript virtual machine, it's important to know how to define and extend Unreal Engine classes inside of TypeScript.