Skip to content

Commit

Permalink
add english translations to docs (Tencent#531)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeseese authored Nov 5, 2021
1 parent 5fa50ca commit d35d87c
Show file tree
Hide file tree
Showing 5 changed files with 1,040 additions and 0 deletions.
97 changes: 97 additions & 0 deletions doc/en/unreal/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# FAQ

Below is a translated version of the original docs by Incanta Games. The translation is mainly done with Google Translate, but then modified by hand to try to make sense of what Google Translate is missing.

## `new (std::nothrow) int[0] return nullptr, try fix it!` Warning

Unreal overloads the `new` operator, and processing does not comply with C ++ specification. Initializing an empry array of `nothrow` mode `new`, returns `nullptr`, where it should return a valid value. It should only return `nullptr` if we're OOM (Out of Memory). (Incanta guessing this next sentence; I don't think it's critical here) The modification let's comply with the C++ specification, where V8 is misunderstanding OOM, thereby abort. This issues seems to be prevalent on Windows machines, and this issue is also confirmed by Epic official.

If the Puerts discovery engine finds this bug, it will repair this problem by overwriting the memory allocation behavior, and prints `new (std::nothrow) int [0] return nullptr, try fix it!` warning to the Output Log. The warning only prompts the user that this exists, and there is no impact.

### Notes from Incanta Games translating this

The warning text is not telling the developer "to fix the issue", but rather it's telling the developer that Puerts is "trying to fix the issue."

In actuality this warning is presented when the `JsEnvModule.cpp` initializes:

``` c++
int * Dummy = new (std::nothrow) int[0];
if (!Dummy)
{
UE_LOG(JsEnvModule, Warning, TEXT("new (std::nothrow) int[0] return nullptr, try fix it!"));
MallocWrapper = new FMallocWrapper(GMalloc);
GMalloc = MallocWrapper;
}
delete[] Dummy;
```

Per the documentation at https://www.cplusplus.com/reference/new/nothrow/, `new (std::nothrow) int[0]` shouldn't fail, and Puerts is trying to trying to fix the issue by providing a wrapper around `GMalloc` (which is Unreal's memory allocation overload). The `FMallocWrapper` merely checks to see if the size is `0` when allocating, and if so, it defaults it to `1`. This fix is meant to prevent further uses of `GMalloc` that use an array size of `0` during allocation from failing.

## Some plugins can not be used in automatic building mode

The Puerts module will traverse modules that are loaded before beforehand, so if your plugins or C++ modules are not being used, you can do one of two things:

1. Set the module to load at an earlier phase
1. After your module is started, you can call Puerts to reinitialize by calling the below function:

``` c++
IPuertsModule::Get().InitExtensionMethodsMap();
```

## Check WaitDebugger Option

(Incanta here) I'm not quite sure what the original author is trying to say here. There is a `WaitDebugger` setting for the plugin that is actually set to `false` with the latest checkout (Nov 4 2021) of the source branch. It appears that what this feature does is tell the V8 engine to "Break Before Start" and not run any JS until a debugger attaches.

Below is the raw google translate:

> This option is a snap process to wait for the debugger connection, and even go down.
>
> If you haven't configured the debugger yet, I accidentally selected this option, and there is no way to go to remove this option. At this time, you can turn off the process and open it. `Config\DefaultPuerts.ini` Bundle WaitDebugger Change to False。

## ts Blueprint StaticClass transfer, return UClass Use non-conformity

The TS class is not a StaticClass method, so the StaticClass call is actually the first class with the StaticClass method on the inheritance chain, and the returned is also UCLASS where the StaticClass method is located.

I don't understand this may cause some misunderstanding: For example, the object I created is a non-sub-class method, I inevitably CreateDefaultSubiTSubject reporting is Abstract, unable to create.

The correct approach should be loaded by UE.CLASS.LOAD ("path / to / your / blueprint / file").

## MacOS prompts "libv8.dylib cannot be opened because the developer cannot be verified"

(Incanta here) The original author is basically just telling you to tell the Mac firewall/antivirus to trust the library file by navigating to where ever the `dylib` file is (ususally `<YourProjectFolder>/Plugins/Puerts/ThirdParty/V8/Lib/macosdylib`), and execute the below command to authorize the files:

``` bash
sudo xattr -r -d com.apple.quarantine *.dylib
```

However, it should be noted that Puerts no longer uses shared `.dylib` files in favor of static `.a` libraries, and the original path has changed to `<YouProjectFolder>/Plugins/Puerts/ThirdParty/Library/V8/macOS` and you may have the issue with the FFI dependency too: `<YouProjectFolder>/Plugins/Puerts/ThirdParty/Library/ffi/macOS`. Instead of using `*.dylib` in the above command, you may need to use `*.a`. **HOWEVER**, it's likely you won't run into this issue as static libraries are not executed, but compiled in.

## "Project could not compiled. Try rebuilding from source manually"

(Incanta here). The original author is trying to tell you what to do if you receive this error, however, I am giving you a completely different set of instructions, that are hopefully clearer with more details (I'm also a plugin developer and understand the issue here).

This issue is likely happening because you tried to install Puerts in a Blueprint-only project, but since Puerts is built using C++, you'll need to compile the plugin yourself. To do that, you need to do a few steps; I'll give you a link to a guide below:
1. Download the necessary Visual Studio compiler dependencies.
1. Convert your project to a C++ project. This doesn't really do anything other than tell the engine that your project has C++ modules. You can still use Blueprints exactly how you did before.
1. Load the project again and let it compile the plugin for you.

You can follow my instructions on how to install plugins as a project plugin here: https://wiki.incanta.games/en/plugins/install-as-project-plugin

## After packaging, some fields cannot be found.

> Incanta here, below is my modified translation of the Google Translate. I kind of find it difficult to believe that UE would package your variable names with modified casing, so take this with a grain of salt and test on your own (I have not tested this). I know that UE displays variable and function names like `countThis` => `Count This` in the blueprint editor, but I didn't think it *actually changes* the exported symbol name. The author is saying that in some cases your `count` variables may get renamed to `Count` after packaging, causing references to `count` (i.e. through your JS) to be invalid since the variable got changed on you.
Usually this is caused by how Unreal processes variable names of the type `FName` (or just `Name` in blueprints) while running in the editor vs running in the packaged runtime. In the editor, the default is case sensitive, but at runtime it's case insensitive.

For example, you create a blueprint class and add a field called `count`. You write some BP code to under the BP to test it all. The field `count` can be referenced in PIE, and is running as normal.

After packaging, if you have access to this blueprint, there is already another place to initialize a `Count` field, then when you visit this blueprint, this field will be called `Count`. This happens because `FName.ToString` returns the first construction `FNameEntered` strings, (Incanta here, I can't decipher the meaning with the rest of Google Translate; what remains is raw) so as long as it is turned to lowercase and the first time FName It is the first time.

So you don't exist in the `count` field accessed on the script (becoming a `count` field).

## Generate buttons do not display in UE5 Early Access

UE5 EA changed the behavior of how the toolbar works, causing a failure. Your options are to either wait for Epic to fix the issue or modify the plugin to change how the button renders in the engine.

You can work around this by using the console command: `Puerts.Gen`
269 changes: 269 additions & 0 deletions doc/en/unreal/interact_with_uclass.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# TypeScript and engine interaction

Below is a translated version of the original docs by Incanta Games. The translation is mainly done with Google Translate, but then modified by hand to try to make sense of what Google Translate is missing.

All C++/blueprints of type `UCLASS`, `UPROPERTY`, `UFUNCTION`, `USTRUCT`, `UENUM`, can be accessed directly with TypeScript.

However, for any C++ constructs (attributes, methods, classes, structs, enums) not marked with the associated `UXXXXX` described above, you'll need follow the instructions in [template_binding.md](./template_binding.md).

## Generate TypeScript declaration files

- C++/blueprints are called by TypeScript, in addition to the C++ class is a resident memory, the blueprint requires manual loading, the method/attribute access on other objects, etc.
- Click the following button to make a declaration
![puerts_gen_dts.png](../../../pic/puerts_gen_dts.png)
- Alternatively, a declaration file can also be generated with the console command: `Puerts.Gen`
- The plugin doesn't support the button being shown for UE5 EA currently, so you'll have to use the console command regardless.

## Members and functions

``` typescript
// Object construct
let obj = new UE.MainObject();

// Member visit
console.log("before set", obj.MyString)
obj.MyString = "PPPPP";
console.log("after set", obj.MyString)

// Simple type parameter function
let sum = obj.Add(100, 300);
console.log('sum', sum)

// Complex type parameter function
obj.Bar(new UE.Vector(1, 2, 3));

// Quote type parameter function
let vectorRef = $ref(new UE.Vector(1, 2, 3))
obj.Bar2(vectorRef);
obj.Bar($unref(vectorRef));

// Static function
let str1 = UE.JSBlueprintFunctionLibrary.GetName();
let str2 = UE.JSBlueprintFunctionLibrary.Concat(', ', str1);
UE.JSBlueprintFunctionLibrary.Hello(str2);

// enumerate
obj.EnumTest(UE.EToTest.V1);
```

## Blueprint & Other Resources Load

``` typescript
// Load a blueprint class
let bpClass = UE.Class.Load('/Game/StarterContent/TestBlueprint.TestBlueprint_C');
// UE.XXX.Load is equivalent to writing LoadObject<XXX> in C++, so UE.Class.Load is equivalent to C++'s LoadObject<UCLASS>()
let bpActor = world.SpawnActor(bpClass, undefined, UE.ESpawnActorCollisionHandlingMethod.Undefined, undefined, undefined) as UE.TestBlueprint_C;

// Load a particle system
let bulletImpact = UE.ParticleSystem.Load("/Game/BlockBreaker/ParticleSystems/PS_BulletImpact");

// Load a static mesh
let rifle = UE.StaticMesh.Load("/Game/BlockBreaker/Meshes/SM_Rifle");
```

## TArray, TSet, TMap

### Create

``` typescript
// TArray<int>
let a2 = UE.NewArray(UE.BuiltinInt);

// TArray<FString>
let a3 = UE.NewArray(UE.BuiltinString);

// TArray<UActor>
let a4 = UE.NewArray(UE.Actor);

// TArray<FVector>
let a5 = UE.NewArray(UE.Vector);

// TSet<FString>
let s1 = UE.NewSet(UE.BuiltinString);

// TMap<FString, int>
let m1 = UE.NewMap(UE.BuiltinString, UE.BuiltinInt);
```

### Access, press the automatic prompt of the IDE to access the container (Incanta here: not quite sure what this Google Translate means, but it looks like there's just some demonstration of using the objects generated in the above examples)

``` typescript
a2.Add(888);
a2.Set(0, 7);
console.log(a2.Num());
m1.Add("John", 0)
m1.Add("Che", 1)
console.log(m1.Get("John"))
```

## ArrayBuffer

Handling network messages need this

### Access a C++ FArrayBuffer in TypeScript

If you have the following C++ code:

``` c++
UPROPERTY()
FArrayBuffer ArrayBuffer;

// Set content
ArrayBuffer.Data = "hello";
ArrayBuffer.Length = 5;
```

Here's how you would access with the `ArrayBuffer` in TypeScript:

``` typescript
let ab = obj.ArrayBuffer;
let u8a1 = new Uint8Array(ab);
for (var i = 0; i < u8a1.length; i++) {
console.log(i, u8a1[i]);
}
```

## TypeScript Passing FArrayBuffer to C++

If you have the C++ function that accepts a `FArrayBuffer`, like below:

``` c++
void UMainObject::ArrayBufferTest(const FArrayBuffer& Ab) const
{
UE_LOG(LogTemp, Warning, TEXT("Ab(%p, %d)"), Ab.Data, Ab.Length);
}
```
You can call this function/emulate the `FArrayBuffer` in TypeScript witha `Uint8Array`:
``` typescript
var arr = new Uint8Array([21,31]);
obj.ArrayBufferTest(arr);
```

## Callback

C++ engine code/modules can actively call TypeScript via `Dynamic_Delegate` and `Dynamic_Multicast_Delegate`, similarly how C++ can call/trigger function/events in Blueprints.

- UI user action, web messages can be notified to TypeScript
- If you want to export a TypeScript function to C++ call, you can also use this

Here's the C++ declaration

``` c++
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNotifyWithInt, int32, A);
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(FString, FNotifyWithStringRet, FString, A);
DECLARE_DYNAMIC_DELEGATE_OneParam(FNotifyWithRefString, FString&, A);

UCLASS()
class PUERTS_UNREAL_DEMO_API AMyActor : public AActor
{
GENERATED_BODY()

public:
UPROPERTY()
FNotifyWithInt NotifyWithInt;

UPROPERTY()
FNotifyWithRefString NotifyWithRefString;

UPROPERTY()
FNotifyWithStringRet NotifyWithStringRet;
//...
};

```
This is how you bind TypeScript to be called when the C++ functions trigger the delegates
``` typescript
function MutiCast1(i) {
console.warn("MutiCast1<<<", i);
}
function MutiCast2(i) {
console.warn("MutiCast2>>>", i);
}
actor.NotifyWithInt.Add(MutiCast1);
actor.NotifyWithInt.Add(MutiCast2);
actor.NotifyWithRefString.Bind((strRef) => {
console.log("NotifyWithRefString", $unref(strRef));
$set(strRef, "out to NotifyWithRefString"); // Reference parameter output
});
actor.NotifyWithStringRet.Bind((inStr) => {
return "////" + inStr;
});
```

The below C++ code triggers the events to call the bound functions in TypeScript

``` c++
NotifyWithInt.Broadcast(0);
NotifyWithStringRet.ExecuteIfBound("hi...");
if (NotifyWithRefString.IsBound())
{
FString Str = TEXT("hello john che ");

NotifyWithRefString.Execute(Str);
UE_LOG(LogTemp, Warning, TEXT("NotifyWithRefString out ? %s"), -Str);
}
```

## Extend function

Unreal has a lot of C++ functions without `UFUNCTION` tags; here are tw, how do this API call?There are two ways:

- Extension functions (recommended)
- Generate a wrap code with Puerts an unpublished code generator (Incanta here, not sure what the Google Translate here means, but I concur the below example is the recommended method)

Take `UObject::GetClass` and `UObject::FindFunction` as an example. Basically the below code is just creating C++ wrappers around the the function and exposing them to Blueprints/TypeScript.

C++ extension

``` c++
// ObjectExtension.h
UCLASS()
class UObjectExtension : public UExtensionMethods
{
GENERATED_BODY()

UFUNCTION(BlueprintCallable, Category = "ObjectExtension")
static UClass * GetClass(UObject * Object);

UFUNCTION(BlueprintCallable, Category = "ObjectExtension")
static UFunction * FindFunction(UObject * Object, FName InName);
};
```

``` c++
// ObjectExtension.cpp
#include "ObjectExtension.h"

UClass * UObjectExtension::GetClass(UObject * Object)
{
return Object->GetClass();
}

UFunction * UObjectExtension::FindFunction(UObject * Object, FName InName)
{
return Object->FindFunction(InName);
}
```
Important:
- Newly built a class inheritance from `UExtensionMethods`
- Incanta here. This is a class built into Puerts that extends the `UBlueprintFunctionLibrary` class. I'm assuming Puerts does some extra processing on child classes of `UExtensionMethods` to allow you do to the fancy `obj.GetClass()` syntax in the below TypeScript example. Pretty cool feature.
- The first argument of the function is the expanded class
When TypeScript is accessed, the member method of accessing an object is similar.
**NOTE:** You need to regenerate TypeScript declaration files for the new C++ extension functions.
``` typescript
let cls = obj.GetClass();
let func = obj.FindFunction("Func");
```
Loading

0 comments on commit d35d87c

Please sign in to comment.