Skip to content

Instantly share code, notes, and snippets.

@emrahgunduz
Created February 2, 2017 16:50
Show Gist options
  • Save emrahgunduz/02c70c25da68e0f1d414d1c030dc0038 to your computer and use it in GitHub Desktop.
Save emrahgunduz/02c70c25da68e0f1d414d1c030dc0038 to your computer and use it in GitHub Desktop.
Javascript - Unreal connection via tick message calls
function Act () {
this.name = "null";
this.type = 0;
this.message = "";
}
var actions = [];
var currentAction;
/**
* Add an action
* @param name string
* @param type int 0 - has no message, 1 - with message
* @param message
*/
function actionAdd ( name, type, message ) {
var a = new Act();
a.name = name;
if ( !!type )
a.type = type;
if ( !!message )
a.message = JSON.stringify( message );
actions.push( a );
}
function actionCount () {
return actions.length;
}
function actionLoadNext () {
currentAction = actions.shift();
}
function actionType () {
return currentAction.type;
}
function actionName () {
return currentAction.name;
}
function actionMessage () {
return currentAction.message;
}
function actionReset () {
actions = [];
}
// Fill out your copyright notice in the Description page of Project Settings.
#include "ExpoKid.h"
#include "JavascriptCaller.h"
#include <string>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
UJavascriptCaller::UJavascriptCaller(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
JsonInObjectData = MakeShareable(new FJsonObject());
JsonOutObjectData = MakeShareable(new FJsonObject());
}
UJavascriptCaller *UJavascriptCaller::Tick()
{
#ifdef EMSCRIPTEN
int actionCount = emscripten_run_script_int("actionCount()");
if (actionCount > 0) {
int currentIndex = 0;
while (currentIndex < actionCount) {
emscripten_run_script("actionLoadNext()");
int actionType = emscripten_run_script_int("actionType()");
char *aAction = emscripten_run_script_string("actionName()");
const char* aActionChar = reinterpret_cast<const char*>(aAction);
std::string aActionStr(aActionChar);
FString aActionFStr = UTF8_TO_TCHAR(aActionStr.c_str());
if (actionType == 1) {
// Action has message
char *aMessage = emscripten_run_script_string("actionMessage()");
const char* aMessageChar = reinterpret_cast<const char*>(aMessage);
std::string aMessageStr(aMessageChar);
FString ReceivedString = UTF8_TO_TCHAR(aMessageStr.c_str());
UE_LOG(EXPOKID, Warning, TEXT("[JavascriptCaller::Tick] > Answer for action %s : %s"), UTF8_TO_TCHAR(aActionStr.c_str()), UTF8_TO_TCHAR(aMessageStr.c_str()));
JsonInObjectData = MakeShareable(new FJsonObject());
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(ReceivedString);
bool isDeserialized = FJsonSerializer::Deserialize(JsonReader, JsonInObjectData);
if (!(isDeserialized && JsonInObjectData.IsValid())) {
UE_LOG(EXPOKID, Error, TEXT("[JavascriptCaller::Tick] > JSON is not valid"));
}
}
else {
UE_LOG(EXPOKID, Warning, TEXT("[JavascriptCaller::Tick] > Answer for action %s"), UTF8_TO_TCHAR(aActionStr.c_str()));
}
OnJavascriptActionReceived.Broadcast(aActionFStr);
currentIndex++;
}
}
#endif
return this;
}
UJavascriptCaller *UJavascriptCaller::CallJavascript(FString jsMethodName)
{
std::string jsMethodNameStr(TCHAR_TO_UTF8(*jsMethodName));
jsMethodNameStr.append("()");
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascript] > A javascript method is called: %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()));
#ifdef EMSCRIPTEN
const char * jsMethodNameChar = jsMethodNameStr.c_str();
char *Buffer;
Buffer = emscripten_run_script_string(jsMethodNameChar);
const char* buf = reinterpret_cast<const char*>(Buffer);
std::string cstr(buf);
FString ReceivedString = UTF8_TO_TCHAR(cstr.c_str());
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascript] > Returned answer for %s : %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()), UTF8_TO_TCHAR(cstr.c_str()));
JsonInObjectData = MakeShareable(new FJsonObject());
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(ReceivedString);
bool isDeserialized = FJsonSerializer::Deserialize(JsonReader, JsonInObjectData);
if (!(isDeserialized && JsonInObjectData.IsValid())) {
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascript] > JavascriptCaller::CallJavascript JSON is not valid"));
}
#endif
return this;
}
UJavascriptCaller *UJavascriptCaller::CallJavascriptVoid(FString jsMethodName)
{
std::string jsMethodNameStr(TCHAR_TO_UTF8(*jsMethodName));
jsMethodNameStr.append("()");
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptVoid] > A javascript method is called: %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()));
#ifdef EMSCRIPTEN
const char * jsMethodNameChar = jsMethodNameStr.c_str();
emscripten_run_script(jsMethodNameChar);
#endif
return this;
}
UJavascriptCaller *UJavascriptCaller::CallJavascriptWithMessage(FString jsMethodName)
{
if (!JsonOutObjectData.IsValid()) {
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessage] > Outgoing JSON is not valid"));
return this;
}
FString OutputString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&OutputString);
WriteObject(JsonWriter, "", new FJsonValueObject(JsonOutObjectData));
JsonWriter->Close();
std::string OutputStringStr(TCHAR_TO_UTF8(*OutputString));
std::string jsMethodNameStr(TCHAR_TO_UTF8(*jsMethodName));
jsMethodNameStr.append("('" + OutputStringStr + "')");
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\n'), jsMethodNameStr.end());
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\r'), jsMethodNameStr.end());
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\t'), jsMethodNameStr.end());
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessage] > A javascript method is called: %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()));
#ifdef EMSCRIPTEN
const char * jsMethodNameChar = jsMethodNameStr.c_str();
char *Buffer;
Buffer = emscripten_run_script_string(jsMethodNameChar);
const char* buf = reinterpret_cast<const char*>(Buffer);
std::string cstr(buf);
FString ReceivedString = UTF8_TO_TCHAR(cstr.c_str());
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessage] > Returned answer for %s : %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()), UTF8_TO_TCHAR(cstr.c_str()));
JsonInObjectData = MakeShareable(new FJsonObject());
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(ReceivedString);
bool isDeserialized = FJsonSerializer::Deserialize(JsonReader, JsonInObjectData);
if (!(isDeserialized && JsonInObjectData.IsValid())) {
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessage] > JavascriptCaller::CallJavascriptWithMessage JSON is not valid"));
}
#endif
return this;
}
UJavascriptCaller *UJavascriptCaller::CallJavascriptWithMessageVoid(FString jsMethodName)
{
if (!JsonOutObjectData.IsValid()) {
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessageVoid] > Outgoing JSON is not valid"));
return this;
}
FString OutputString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&OutputString);
WriteObject(JsonWriter, "", new FJsonValueObject(JsonOutObjectData));
JsonWriter->Close();
std::string OutputStringStr(TCHAR_TO_UTF8(*OutputString));
std::string jsMethodNameStr(TCHAR_TO_UTF8(*jsMethodName));
jsMethodNameStr.append("('" + OutputStringStr + "')");
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\n'), jsMethodNameStr.end());
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\r'), jsMethodNameStr.end());
jsMethodNameStr.erase(std::remove(jsMethodNameStr.begin(), jsMethodNameStr.end(), '\t'), jsMethodNameStr.end());
UE_LOG(EXPOKID, Error, TEXT("[UJavascriptCaller::CallJavascriptWithMessageVoid] > A javascript method is called: %s"), UTF8_TO_TCHAR(jsMethodNameStr.c_str()));
#ifdef EMSCRIPTEN
const char * jsMethodNameChar = jsMethodNameStr.c_str();
emscripten_run_script(jsMethodNameChar);
#endif
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageInit()
{
JsonOutObjectData.Reset();
JsonOutObjectData = MakeShareable(new FJsonObject());
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageSetString(const FString &key, const FString &value)
{
JsonOutObjectData->SetStringField(*key, *value);
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageGetString(const FString &key, FString &value)
{
value = JsonInObjectData->GetStringField(key);
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageGetInteger(const FString &key, int32 &value)
{
value = (int32)JsonInObjectData->GetNumberField(key);
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageGetFloat(const FString &key, float &value)
{
value = (float)JsonInObjectData->GetNumberField(key);
return this;
}
UJavascriptCaller *UJavascriptCaller::JavascriptJsonMessageGetBoolean(const FString &key, bool &value)
{
value = JsonInObjectData->GetBoolField(key);
return this;
}
TArray<FString> UJavascriptCaller::JavascriptJsonMessageGetStringArray(const FString &key)
{
TArray<FString> stringArray;
const TArray<TSharedPtr<FJsonValue>> *arrayPtr;
if (JsonInObjectData->TryGetArrayField(*key, arrayPtr)) {
for (int32 i = 0; i < arrayPtr->Num(); i++) {
stringArray.Add((*arrayPtr)[i]->AsString());
}
}
return stringArray;
}
TArray<int32> UJavascriptCaller::JavascriptJsonMessageGetIntegerArray(const FString &key)
{
TArray<int32> stringArray;
const TArray<TSharedPtr<FJsonValue>> *arrayPtr;
if (JsonInObjectData->TryGetArrayField(*key, arrayPtr)) {
for (int32 i = 0; i < arrayPtr->Num(); i++) {
stringArray.Add((int const &)(*arrayPtr)[i]->AsNumber());
}
}
return stringArray;
}
TArray<float> UJavascriptCaller::JavascriptJsonMessageGetFloatArray(const FString &key)
{
TArray<float> floatArray;
const TArray<TSharedPtr<FJsonValue>> *arrayPtr;
if (JsonInObjectData->TryGetArrayField(*key, arrayPtr)) {
for (int32 i = 0; i < arrayPtr->Num(); i++) {
floatArray.Add((float const &)(*arrayPtr)[i]->AsNumber());
}
}
return floatArray;
}
void UJavascriptCaller::WriteObject(TSharedRef<TJsonWriter<TCHAR>> writer, FString key, FJsonValue *value)
{
if (value->Type == EJson::String) {
// Write simple string entry, don't need a key when it isn't set
if (key.Len() > 0) {
writer->WriteValue(key, value->AsString());
}
else {
writer->WriteValue(value->AsString());
}
}
else if (value->Type == EJson::Object) {
// Write object entry
if (key.Len() > 0) {
writer->WriteObjectStart(key);
}
else {
writer->WriteObjectStart();
}
// Loop through all the values in the object data
TSharedPtr<FJsonObject> objectData = value->AsObject();
for (auto objectValue = objectData->Values.CreateIterator(); objectValue; ++objectValue) {
// Using recursion to write the key and value to the writer
WriteObject(writer, objectValue.Key(), objectValue.Value().Get());
}
writer->WriteObjectEnd();
}
else if (value->Type == EJson::Array) {
// Process array entry
writer->WriteArrayStart(key);
TArray<TSharedPtr<FJsonValue>> objectArray = value->AsArray();
for (int32 i = 0; i < objectArray.Num(); i++) {
// Use recursion with an empty key to process all the values in the array
WriteObject(writer, "", objectArray[i].Get());
}
writer->WriteArrayEnd();
}
}
#pragma once
#ifdef EMSCRIPTEN
#include <emscripten.h>
#endif
#include "Delegate.h"
#include "Object.h"
#include "JavascriptCaller.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FJavascriptCaller_OnAction, FString, action);
UCLASS(Blueprintable, BlueprintType)
class EXPOKID_API UJavascriptCaller : public UObject
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "Javascript")
FJavascriptCaller_OnAction OnJavascriptActionReceived;
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *Tick();
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *CallJavascript(FString jsMethodName);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *CallJavascriptVoid(FString jsMethodName);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *CallJavascriptWithMessage(FString jsMethodName);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *CallJavascriptWithMessageVoid(FString jsMethodName);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageGetString(const FString &key, FString &value);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageGetInteger(const FString &key, int32 &value);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageGetFloat(const FString &key, float &value);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageGetBoolean(const FString &key, bool &value);
UFUNCTION(BlueprintCallable, Category = "Javascript")
TArray<FString> JavascriptJsonMessageGetStringArray(const FString &key);
UFUNCTION(BlueprintCallable, Category = "Javascript")
TArray<int32> JavascriptJsonMessageGetIntegerArray(const FString &key);
UFUNCTION(BlueprintCallable, Category = "Javascript")
TArray<float> JavascriptJsonMessageGetFloatArray(const FString &key);
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageInit();
UFUNCTION(BlueprintCallable, Category = "Javascript")
UJavascriptCaller *JavascriptJsonMessageSetString(const FString &key, const FString &value);
private:
TSharedPtr<FJsonObject> JsonInObjectData;
TSharedPtr<FJsonObject> JsonOutObjectData;
void WriteObject(TSharedRef<TJsonWriter<TCHAR>> writer, FString key, FJsonValue *value);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment