keywords: UE4, Puerts Notes

Common

Examples

Unreal example: How to debug TypeScript based on Unreal PuerTS in IntelliJ IDEA
https://github.com/dawnarc/PuertsGame

C++ server example:《c++游戏服务器嵌入v8 js引擎胎教级教程》配套代码
https://github.com/chexiongsheng/v8_embedding_test

Performance

Benchmarking of C++, SLUA, Unreal.js, Puerts
https://github.com/Tencent/puerts/issues/443

Comparison:
  • The speed of interacting with C++ or UE4 Blueprint: Puerts is 11 times sLua, 5 times Unreal.js.
  • The speed of running pure script: Puerts is 67 times sLua, has the same performance of Unreal.js.
  • Pure C++ vs. Pure TypeScript (PuerTS): C++ is 3.5 times PuerTS, as a comparison, C++ is twice performance of nativing Blueprint of UE4.
Extension: how to add(bind) other Unreal API in ue.d.ts

Add UUserWidget::GetWidgetFromName() in ue.d.ts:

#include "CoreMinimal.h"
#include "Binding.hpp"
#include "UEDataBinding.hpp"
#include "Blueprint/UserWidget.h"

UsingUClass(UObject);
UsingUClass(UClass);
#if !defined(ENGINE_INDEPENDENT_JSENV)
UsingUClass(UWorld);    // for return type
UsingUClass(USceneComponent);
UsingUClass(UActorComponent);
UsingUClass(UUserWidget);
UsingUClass(UWidget);
#endif

struct AutoRegisterForUE
{
    AutoRegisterForUE()
    {
        puerts::DefineClass<UObject>()
#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 23
            .Method("CreateDefaultSubobject",
                SelectFunction(UObject * (UObject::*) (FName, UClass*, UClass*, bool, bool), &UObject::CreateDefaultSubobject))
#else
            .Method("CreateDefaultSubobject", SelectFunction(UObject * (UObject::*) (FName, UClass*, UClass*, bool, bool, bool),
                                                  &UObject::CreateDefaultSubobject))
#endif
            .Method("GetName", SelectFunction(FString(UObjectBaseUtility::*)() const, &UObjectBaseUtility::GetName))
            .Method("GetOuter", MakeFunction(&UObject::GetOuter))
            .Method("GetClass", MakeFunction(&UObject::GetClass))
#if !defined(ENGINE_INDEPENDENT_JSENV)
            .Method("GetWorld", MakeFunction(&UObject::GetWorld))
#endif
            .Register();

#if !defined(ENGINE_INDEPENDENT_JSENV)
        puerts::DefineClass<USceneComponent>()
            .Method("SetupAttachment", MakeFunction(&USceneComponent::SetupAttachment))
            .Register();

        puerts::DefineClass<UActorComponent>()
            .Method("RegisterComponent", MakeFunction(&UActorComponent::RegisterComponent))
            .Register();
        
        puerts::DefineClass<UUserWidget>()
            .Method("GetWidgetFromName", SelectFunction(UWidget* (UUserWidget::*) (const FName&) const, &UUserWidget::GetWidgetFromName))
            .Register();
#endif
    }
};

AutoRegisterForUE _AutoRegisterForUE__;
Binding

Dynamic delegate VS Call function directly
https://github.com/Tencent/puerts/issues/749

Issues

Crash on startup : AsyncLoadingThread.RecursionNotAllowed.Increment() == 1

Packaged application startup failed with pop error (works right in editor):

Assertion failed: AsyncLoadingThread.RecursionNotAllowed.Increment() == 1 [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp] [Line: 3992] 

Solution:
Load object in ReceiveBeginPlay instead of loading object in Constructor.

TArry.Get returns value type

C++

USTRUCT(BlueprintType)
struct FMyStruct
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY()
    FString ID;
}

TypeScript:

public List: UE.TArray<UE.FMyStruct>;

Constructor()
{
    this.List = UE.NewArray(UE.FMyStruct);
    this.List.Add(new UE.FMyStruct);
}

example:

let Data = List.Get(0);
Data.ID = "foo";

Data is a copy of List.Get(0) (value type), not a reference type, so Data.ID = "foo"; would not effect the data which was inside the List.

Reference:
https://github.com/Tencent/puerts/issues/703

Error: can not find XXX

Error on playing game

Puerts: Display: (0x000001D0EEC7A250) Bind module [Character/TS_AnimInstance] 
Puerts: Error: (0x000001D0EEC7A250) load module [Character/TS_AnimInstance] exception D:\MyProj\Content\JavaScript\puerts\modular.js:96: Error: can not find [Character/TS_AnimInstance]
Error: can not find [Character/TS_AnimInstance]
    at require (D:\MyProj\Content\JavaScript\puerts\modular.js:96:30)

Caused by:
There’s some compilation error in TypeScript source caused by C++ interface, e.g. a function invoked in TS was removed in C++. Fix the compilation error then re-generate ue.d.ts.

Solution:
Fix the compilation error. Build C++ project if C++ source was modified.

You’d better close editor and remove tsconfig.json, then re-execute command node enable_puerts_module.js.
Or generate ue.d.ts, then restart editor, then regenerate ue.d.ts again.

Crash: internal::FixStaleLeftTrimmedHandlesVisitor::VisitRootPointers()

Crash callstack:

PuertsTestError: === Critical error: ===
PuertsTestError: 
PuertsTestError: Fatal error!
PuertsTestError: 
PuertsTestError: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0xffffffffffffffff
PuertsTestError: 
PuertsTestError: [Callstack] 0x00007ff74197ccb9 Test.exe!v8::internal::FixStaleLeftTrimmedHandlesVisitor::VisitRootPointers() []
PuertsTestError: [Callstack] 0x00007ff74192193f Test.exe!v8::internal::HandleScopeImplementer::IterateThis() []
PuertsTestError: [Callstack] 0x00007ff741971c8e Test.exe!v8::internal::Heap::IterateStrongRoots() []
PuertsTestError: [Callstack] 0x00007ff741971a72 Test.exe!v8::internal::Heap::IterateRoots() []
PuertsTestError: [Callstack] 0x00007ff741b58632 Test.exe!v8::internal::ScavengerCollector::CollectGarbage() []
PuertsTestError: [Callstack] 0x00007ff74197a1b2 Test.exe!v8::internal::Heap::Scavenge() []
PuertsTestError: [Callstack] 0x00007ff741974829 Test.exe!v8::internal::Heap::PerformGarbageCollection() []
PuertsTestError: [Callstack] 0x00007ff741968d53 Test.exe!v8::internal::Heap::CollectGarbage() []
PuertsTestError: [Callstack] 0x00007ff741967084 Test.exe!v8::internal::Heap::AllocateRawWithRetryOrFail() []
PuertsTestError: [Callstack] 0x00007ff741984bed Test.exe!v8::internal::Factory::CopyJSObjectWithAllocationSite() []
PuertsTestError: [Callstack] 0x00007ff741984a81 Test.exe!v8::internal::Factory::CopyJSObject() []
PuertsTestError: [Callstack] 0x00007ff7419c3257 Test.exe!v8::internal::ApiNatives::InstantiateFunction() []
PuertsTestError: [Callstack] 0x00007ff7419c35a5 Test.exe!v8::internal::ApiNatives::InstantiateObject() []
PuertsTestError: [Callstack] 0x00007ff741aa2308 Test.exe!v8::internal::CustomArguments<v8::FunctionCallbackInfo<v8::Value> >::GetReturnValue<v8::internal::Object>() []
PuertsTestError: [Callstack] 0x00007ff741aa3807 Test.exe!v8::internal::Builtins::InvokeApiFunction() []
PuertsTestError: [Callstack] 0x00007ff741945a69 Test.exe!v8::internal::Execution::CallWasm() []
PuertsTestError: [Callstack] 0x00007ff741945f7b Test.exe!v8::internal::Execution::New() []
PuertsTestError: [Callstack] 0x00007ff7419298b6 Test.exe!v8::Function::NewInstanceWithSideEffectType() []
PuertsTestError: [Callstack] 0x00007ff741929240 Test.exe!v8::Function::NewInstance() []
PuertsTestError: [Callstack] 0x00007ff73b1c803d Test.exe!puerts::FJsEnvImpl::FindOrAdd() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\JsEnvImpl.cpp:1541]
PuertsTestError: [Callstack] 0x00007ff73b0884b5 Test.exe!puerts::internal::FuncCallHelper<std::pair<UObject *,std::tuple<FName,UClass *,UClass *,bool,bool> >,0>::callMethod<UObject,UObject * (__cdecl UObject::*)(FName,UClass *,UClass *,bool,bool),0,1,2,3,4>() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Public\Binding.hpp:403]
PuertsTestError: [Callstack] 0x00007ff73b185dd3 Test.exe!puerts::FuncCallWrapper<UObject * (__cdecl UObject::*)(FName,UClass *,UClass *,bool,bool),&UObject::CreateDefaultSubobject>::call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Public\Binding.hpp:464]
PuertsTestError: [Callstack] 0x00007ff741aa3250 Test.exe!v8::internal::FunctionCallbackArguments::Call() []
PuertsTestError: [Callstack] 0x00007ff741aa273f Test.exe!v8::internal::CustomArguments<v8::FunctionCallbackInfo<v8::Value> >::GetReturnValue<v8::internal::Object>() []
PuertsTestError: [Callstack] 0x00007ff741aa2abf Test.exe!v8::internal::Builtin_HandleApiCallAsFunction() []
PuertsTestError: [Callstack] 0x00007ff741aa28be Test.exe!v8::internal::Builtin_HandleApiCall() []
PuertsTestError: [Callstack] 0x00007ff74216ddbd Test.exe!Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit() []
PuertsTestError: [Callstack] 0x00007ff7420ec8ec Test.exe!Builtins_InterpreterEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420ec8ec Test.exe!Builtins_InterpreterEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9ea1 Test.exe!Builtins_JSEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9a8c Test.exe!Builtins_JSEntry() []
PuertsTestError: [Callstack] 0x00007ff741945cd3 Test.exe!v8::internal::Execution::CallWasm() []
PuertsTestError: [Callstack] 0x00007ff741945646 Test.exe!v8::internal::Execution::Call() []
PuertsTestError: [Callstack] 0x00007ff741907eeb Test.exe!v8::Function::Call() []
PuertsTestError: [Callstack] 0x00007ff73b1e04ca Test.exe!puerts::FJsEnvImpl::TsConstruct() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\JsEnvImpl.cpp:1809]
PuertsTestError: [Callstack] 0x00007ff73b219132 Test.exe!UTypeScriptGeneratedClass::ObjectInitialize() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\TypeScriptGeneratedClass.cpp:71]
PuertsTestError: [Callstack] 0x00007ff73b21c462 Test.exe!UTypeScriptGeneratedClass::StaticConstructor() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\TypeScriptGeneratedClass.cpp:53]
PuertsTestError: [Callstack] 0x00007ff73cb96a6b Test.exe!StaticConstructObject_Internal() []
PuertsTestError: [Callstack] 0x00007ff7400aa368 Test.exe!UWorld::SpawnActor() []
PuertsTestError: [Callstack] 0x00007ff73ff161d3 Test.exe!UGameplayStatics::BeginDeferredActorSpawnFromClass() []
PuertsTestError: [Callstack] 0x00007ff740a474c0 Test.exe!UGameplayStatics::execBeginDeferredActorSpawnFromClass() []
PuertsTestError: [Callstack] 0x00007ff73c8f6839 Test.exe!UFunction::Invoke() []
PuertsTestError: [Callstack] 0x00007ff73cb79d8c Test.exe!UObject::ProcessEvent() []
PuertsTestError: [Callstack] 0x00007ff73b0971ec Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:259]
PuertsTestError: [Callstack] 0x00007ff73b097450 Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:205]
PuertsTestError: [Callstack] 0x00007ff741aa3250 Test.exe!v8::internal::FunctionCallbackArguments::Call() []
PuertsTestError: [Callstack] 0x00007ff741aa273f Test.exe!v8::internal::CustomArguments<v8::FunctionCallbackInfo<v8::Value> >::GetReturnValue<v8::internal::Object>() []
PuertsTestError: [Callstack] 0x00007ff741aa2abf Test.exe!v8::internal::Builtin_HandleApiCallAsFunction() []
PuertsTestError: [Callstack] 0x00007ff741aa28be Test.exe!v8::internal::Builtin_HandleApiCall() []
PuertsTestError: [Callstack] 0x00007ff74216ddbd Test.exe!Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit() []
PuertsTestError: [Callstack] 0x00007ff7420ec8ec Test.exe!Builtins_InterpreterEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9ea1 Test.exe!Builtins_JSEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9a8c Test.exe!Builtins_JSEntry() []
PuertsTestError: [Callstack] 0x00007ff741945cd3 Test.exe!v8::internal::Execution::CallWasm() []
PuertsTestError: [Callstack] 0x00007ff741945646 Test.exe!v8::internal::Execution::Call() []
PuertsTestError: [Callstack] 0x00007ff741907eeb Test.exe!v8::Function::Call() []
PuertsTestError: [Callstack] 0x00007ff73b098408 Test.exe!puerts::FFunctionTranslator::CallJs() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:452]
PuertsTestError: [Callstack] 0x00007ff73b1cb5f0 Test.exe!puerts::FJsEnvImpl::InvokeTsMethod() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\JsEnvImpl.cpp:1982]
PuertsTestError: [Callstack] 0x00007ff73b22c95f Test.exe!UTypeScriptGeneratedClass::execCallJS() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\TypeScriptGeneratedClass.cpp:27]
PuertsTestError: [Callstack] 0x00007ff73c8f6839 Test.exe!UFunction::Invoke() []
PuertsTestError: [Callstack] 0x00007ff73cb79d8c Test.exe!UObject::ProcessEvent() []
PuertsTestError: [Callstack] 0x00007ff73b0971ec Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:259]
PuertsTestError: [Callstack] 0x00007ff73b097450 Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:205]
PuertsTestError: [Callstack] 0x00007ff741aa3250 Test.exe!v8::internal::FunctionCallbackArguments::Call() []
PuertsTestError: [Callstack] 0x00007ff741aa273f Test.exe!v8::internal::CustomArguments<v8::FunctionCallbackInfo<v8::Value> >::GetReturnValue<v8::internal::Object>() []
PuertsTestError: [Callstack] 0x00007ff741aa2abf Test.exe!v8::internal::Builtin_HandleApiCallAsFunction() []
PuertsTestError: [Callstack] 0x00007ff741aa28be Test.exe!v8::internal::Builtin_HandleApiCall() []
PuertsTestError: [Callstack] 0x00007ff74216ddbd Test.exe!Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit() []
PuertsTestError: [Callstack] 0x00007ff7420ec8ec Test.exe!Builtins_InterpreterEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9ea1 Test.exe!Builtins_JSEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9a8c Test.exe!Builtins_JSEntry() []
PuertsTestError: [Callstack] 0x00007ff741945cd3 Test.exe!v8::internal::Execution::CallWasm() []
PuertsTestError: [Callstack] 0x00007ff741945646 Test.exe!v8::internal::Execution::Call() []
PuertsTestError: [Callstack] 0x00007ff741907eeb Test.exe!v8::Function::Call() []
PuertsTestError: [Callstack] 0x00007ff73b098408 Test.exe!puerts::FFunctionTranslator::CallJs() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:452]
PuertsTestError: [Callstack] 0x00007ff73b1cb5f0 Test.exe!puerts::FJsEnvImpl::InvokeTsMethod() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\JsEnvImpl.cpp:1982]
PuertsTestError: [Callstack] 0x00007ff73b22c95f Test.exe!UTypeScriptGeneratedClass::execCallJS() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\TypeScriptGeneratedClass.cpp:27]
PuertsTestError: [Callstack] 0x00007ff73c8f6839 Test.exe!UFunction::Invoke() []
PuertsTestError: [Callstack] 0x00007ff73cb79d8c Test.exe!UObject::ProcessEvent() []
PuertsTestError: [Callstack] 0x00007ff73b0971ec Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:259]
PuertsTestError: [Callstack] 0x00007ff73b097450 Test.exe!puerts::FFunctionTranslator::Call() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:205]
PuertsTestError: [Callstack] 0x00007ff741aa3250 Test.exe!v8::internal::FunctionCallbackArguments::Call() []
PuertsTestError: [Callstack] 0x00007ff741aa273f Test.exe!v8::internal::CustomArguments<v8::FunctionCallbackInfo<v8::Value> >::GetReturnValue<v8::internal::Object>() []
PuertsTestError: [Callstack] 0x00007ff741aa2abf Test.exe!v8::internal::Builtin_HandleApiCallAsFunction() []
PuertsTestError: [Callstack] 0x00007ff741aa28be Test.exe!v8::internal::Builtin_HandleApiCall() []
PuertsTestError: [Callstack] 0x00007ff74216ddbd Test.exe!Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit() []
PuertsTestError: [Callstack] 0x00007ff7420ec8ec Test.exe!Builtins_InterpreterEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9ea1 Test.exe!Builtins_JSEntryTrampoline() []
PuertsTestError: [Callstack] 0x00007ff7420e9a8c Test.exe!Builtins_JSEntry() []
PuertsTestError: [Callstack] 0x00007ff741945cd3 Test.exe!v8::internal::Execution::CallWasm() []
PuertsTestError: [Callstack] 0x00007ff741945646 Test.exe!v8::internal::Execution::Call() []
PuertsTestError: [Callstack] 0x00007ff741907eeb Test.exe!v8::Function::Call() []
PuertsTestError: [Callstack] 0x00007ff73b0986c1 Test.exe!puerts::FFunctionTranslator::CallJs() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\FunctionTranslator.cpp:338]
PuertsTestError: [Callstack] 0x00007ff73b1caf80 Test.exe!puerts::FJsEnvImpl::InvokeJsCallback() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\JsEnvImpl.cpp:1668]
PuertsTestError: [Callstack] 0x00007ff73b178ac4 Test.exe!UDynamicDelegateProxy::ProcessEvent() [D:\TestProj\Plugins\Puerts\Source\JsEnv\Private\DynamicDelegateProxy.cpp:23]
PuertsTestError: [Callstack] 0x00007ff73c22113f Test.exe!UITTcpLibrary::Tick() [D:\TestProj\Source\Avatar\Network\ITTcpLibrary.cpp:517]
PuertsTestError: [Callstack] 0x00007ff73c229299 Test.exe!AAvatarGameMode::Tick() [D:\TestProj\Source\Avatar\AvatarGameMode.cpp:28]
PuertsTestError: [Callstack] 0x00007ff73f9f9a3e Test.exe!AActor::TickActor() []
PuertsTestError: [Callstack] 0x00007ff73f9d60ae Test.exe!FActorTickFunction::ExecuteTick() []
PuertsTestError: [Callstack] 0x00007ff7407a0b7b Test.exe!FTickFunctionTask::DoTask() []

Caused by:
Issue of js V8
https://github.com/Tencent/puerts/issues/346
https://github.com/Tencent/puerts/issues/603

Solution:
update v8 to the latest version
https://github.com/Tencent/puerts/blob/master/doc/unreal/install.md


“There's no coming to consciousness without pain.” ― Carl Gustav Jung