UE4 HTML5 C++ intermodulates with JS (source version 4.22)

Catalog

1. js C++.

1.c++ Declaration:

2. Modify HTML5ToolChain.cs file

3. Modify project_template.js file

4. Call

2. Calling js by C++.

1. Inline

1) EM_JS Call:

 2)EM_ASM:

3) EM_ASM_INT, EM_ASM_DOUBLE Has Return Value Method

2.emscripten_run_script() 

3. Modify Source Code

1) In HTML5 JavaScriptFx. C++ function added in H extern "c"

2) In HTML5JavaScriptFx.js implementation of new functions in js

Example 1: Implement c++ call js to get a URL by modifying the source code

Example 2: Transferring data from c++ to js

1) EM_ASM implementation:

2) New methods in HTML5 JavaScriptFx

1. js C++.

1.c++ Declaration:

//Declare c++ Functions
//A declaration may or may not have a return value
//Multiple functions can be declared in extern "c"
//Parameters can be passed to functions
extern "C" {
#ifdef __EMSCRIPTEN__
	EMSCRIPTEN_KEEPALIVE
#endif
	char*  web_get_data()
	{    
		return (char*)BufferArray.GetData();
	}

    void   web_set_data(char* indata,int length)
    {
        if(length>sieof(Data))
        {
            retrun;
        }
        Data = indata;
    }
}

//Note here that returning char* to js must return the uint8 array
//BufferArray is defined as follows
TArray<uint8> BufferArray;

//FString to TArray<uint8>method:
    FString SendBuffer = "SendMessage";
	FTCHARToUTF8 Converter(*SendBuff);
	BufferArray.SetNum(Converter.Length());
	FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());
//This is the official way to view HTML5HTTP. SetContentAsString() in CPP

2. Modify HTML5ToolChain.cs file

In addition to declaring extern "c" functions, you also need to configure the precompiled file in the precompiled configuration, which is compiled in HTML5ToolChain.cs file, find the following two lines)

Result += " -s EXPORTED_FUNCTIONS=\"['_main', '_on_fatal']\"";
Result += " -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['Pointer_stringify', 'writeAsciiToMemory', 'stackTrace']\"";

Add the declared c++ function name to the first line, and note that you need to precede the function name with a horizontal bar'' (

Add a new call method'ccall'in the second line, modified as follows:

Result += " -s EXPORTED_FUNCTIONS=\"['_main', '_on_fatal','_web_get_data','_web_set_data']\"";
Result += " -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['Pointer_stringify', 'writeAsciiToMemory', 'stackTrace','ccall']\"";

3. Modify project_template.js file
 

//Find the declaration here at var UE4
var UE4 = {
	on_fatal: function() {
		try {
			UE4.on_fatal = Module.cwrap('on_fatal', null, ['string', 'string']);
		} catch(e) {
			UE4.on_fatal = function() {};
		}
	}, web_get_data: function () {
		try {
			UE4.web_get_data = Module.ccall('web_get_data', null);
		} catch (e) {
			UE4.web_get_data = function () { };
		}
	},web_set_data: function (indata,inlength) {
		try {
			UE4.web_set_data= Module.ccall('web_set_data', null,['string','string'],['number','number']);
		} catch (e) {
			UE4.web_set_data= function (indata,inlength) { };
		}
	},
};

4. Call

When the above three steps are completed, c++ can be invoked through js

var data = Module.ccall('web_get_data','string');
Module.ccall('web_set_data',null,['string'],['number'])

//Where Module. The values in the call indicate:
//First parameter: function name
//Second parameter: function return value type
//Starting with the third parameter, is the type of parameter value of the function

2. Calling js by C++.

1. Inline

1) EM_JS Call:

//Declare EM_JS
EM_JS(void,string_size,(),{
    var str = "123456";
    console.log(str.length);
});

//Called in the cpp function
string_size();

 2)EM_ASM:

//Called directly in the cpp function
EM_ASM({
    console.log("print data");
});

//Can pass parameters
int32 num = 10;
EM_ASM({
    console.log("Incoming number:"+$0);
},num);

//Multiple parameters can also be passed in
int32 num1 = 10;
bool bstring = true;
EM_ASM({
    console.log("Incoming number:"+$0 + $1);
},num1,bstring);

//Incoming parameter, expressed as $+ number in js

3) EM_ASM_INT, EM_ASM_DOUBLE Has Return Value Method

EM_ASM_INT({
    return 1;
});

EM_ASM_DOUBLE({
    return 1.0;
});

//Other return value methods are the same
//Again, these calls are all passable

2.emscripten_run_script() 

//This way, with EM_ASM is similar, but EM_ASM is more efficient
emscripten_run_script("alert('c++ call!')");

3. Modify Source Code

1) In HTML5 JavaScriptFx. C++ function added in H extern "c"

2) In HTML5JavaScriptFx.js implementation of new functions in js

Note: Implementations in js need to be written in $UE_ In JSlib

Example 1: Implement c++ call js to get a URL by modifying the source code

In HTML5JavaScriptFx. Add void Get_to H URLData (const char* outdata, int outsize);

In HTML5JavaScriptFx.js implementation of new functions in js

//js implementation
UE_GetURLString: function (outdata, outsize) {
	var hre = window.location.search;
	if (hre.length >= outsize)
	{
		console.log("window.location.href size harf then OutSize!!!")
		return;
	}
	hre = decodeURI(hre, "utf-8");
	var newhre = window.btoa(encodeURIComponent(hre));
	console.log(newhre);
	Module.writeAsciiToMemory(newhre, outdata);
},

//It is important to note that Decode64 is used here. If you do not use transcoding, if you have Chinese or some symbols, you will encounter garbled or incomplete information when writing to c++ in memory.

Called in c++:

//By EM_ASM_INT Gets the length of the url
int32 Hlength= EM_ASM_INT({
		var hre = window.location.url;
		var newhre = window.btoa(encodeURIComponent(hre));
		return newhre.length + 1;
});

//Create char based on url length
char OutData[Hlength];

//Call js implementation
UE_GetURLString(OutData, sizeof(OutData));

//Transfer data to FString
FString URL = FString(StringCast<TCHAR>(OutData).Get());

//Decode
FString SaveURL;
FBase64::Decode(URL,SaveULR);
SaveURL = FGenericPlatformHttp::UrlDecode(SaveURL);

//At this point, the URL was successfully retrieved from js

Example 2: Transferring data from c++ to js

1) EM_ASM implementation:

//Calling EM_in a function ASM
//As mentioned above, EM_ASM can pass parameters, so pass them directly

TArray<uint8> BufferArray;
FString SendBuff = "Send Chinese Message";

//Decode64 is required to transcode SendBuff to prevent scrambling
SendBuff = FGenericPlatformHttp::UrlEncode(SendBuff);
SendBuff = FBase64::Encode(SendBuff );

//FString needs to be converted to TArray<uint8>
FTCHARToUTF8 Converter(*SendBuff);
BufferArray.SetNum(Converter.Length());
FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());

//Call EM_ASM
EM_ASM({
    //Convert uint8 array to make js recognizable
    var sdata = Module.HEAP8.subarray($0,$0+$1);
    
    //Convert uint8 array to string    
	var dataString = "";
	for (var i = 0; i < sdata.length; i++) {
		dataString += String.fromCharCode(sdata[i]);
	}
    
    //Decode
    var sendBuff = decodeURIComponent(window.atob(dataString));
    
    //Print raw data
    console.log(sendBuff); 

    //Set URL
    var URL = "http://............"

    //Send to server
    $.ajax({
	    url: URL,
		type: "post",
		data: { data: sendBuff },
		dataType: 'text',
		async: true,
		success: function (res) {
	    	console.log("Send Message Success!");
				// Successful callback function
				//alert('Successful Data Request')
			},
		error: function (XMLHttpRequest, textStatus, errorThrown) {
				// Failed Callback Function
				console.log(XMLHttpRequest, textStatus, errorThrown);
				//alert('Failed to Request Data')
			}
		});

},(char*)BufferArray.GetData(),BufferArray.Num());

2) New methods in HTML5 JavaScriptFx

//In HTML5JavaScriptFx. Add in extern "c" of H
void UE_PostRequest(const char* senddata,int sendlength);


//In HTML5JavaScriptFx. New implementation in JS
//Note that you want to write in $UE_ Inside JSlib
UE_PostRequest:function(senddata,sendlength){
    
    //Convert uint8 array
    var sdata = Module.HEAP8.subarray(senddata, senddata+ sendlength);
    
    //uint8 -> string
	var dataString = "";
	for (var i = 0; i < sdata.length; i++) {
		dataString += String.fromCharCode(sdata[i]);
	}

    //Decode
	var sendBuff = decodeURIComponent(window.atob(dataString));
    console.log(sendBuff);

    //Set URL
    var URL = "http://.......";
	$.ajax({
		url: URL,
		type: "post",
		data: { data: sendBuff },
		dataType: 'text',
		async: true,
		success: function (res) {
			console.log("Send Message Success!");
			// Successful callback function
			//alert('Successful Data Request')
		},
		error: function (XMLHttpRequest, textStatus, errorThrown) {
			// Failed Callback Function
			console.log(XMLHttpRequest, textStatus, errorThrown);
			//alert('Failed to Request Data')
		}
	});
    
}

Called in cpp

TArray<uint8> BufferArray;
FString SendBuff = "Send Chinese Message";

//Decode64 is required to transcode SendBuff to prevent scrambling
SendBuff = FGenericPlatformHttp::UrlEncode(SendBuff);
SendBuff = FBase64::Encode(SendBuff );

//FString needs to be converted to TArray<uint8>
FTCHARToUTF8 Converter(*SendBuff);
BufferArray.SetNum(Converter.Length());
FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());

#ifdef __EMSCRIPTEN__ 
	UE_PostRequest( (char*)BufferArray.GetData(), BufferArray.Num());
#endif	

//At this point, the js method call by c++ is complete

Note: The method that invokes js in cpp, either way, must contain the following header file

#ifdef __EMSCRIPTEN__ otherwise ignore".
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#endif

And when writing, if you call a js method, you must do so, otherwise you will get an error when compiling your project unless you package HTML5 directly

#ifdef __EMSCRIPTEN__ 
	//Calling the js implementation must write here
#endif	

These are the common methods of intermodulation between UE4 c++ and js, which can be found in the engine. I am here to make a collation and summary based on my own project and share it with you. I hope to help you, if there are any errors, I hope to correct them

Keywords: C++ Javascript

Added by sahel on Sun, 16 Jan 2022 07:08:29 +0200