Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve interoperability with JavaScript #1723

Closed
Thaina opened this issue Oct 25, 2020 · 4 comments
Closed

Improve interoperability with JavaScript #1723

Thaina opened this issue Oct 25, 2020 · 4 comments

Comments

@Thaina
Copy link

Thaina commented Oct 25, 2020

Describe the project you are working on:
Web game

Describe the problem or limitation you are having in your project:
Using any asynchronous operation in js side, any library, especially third party

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I wish to have official and homogenous way any godot project would handling async js interop. Both promise and listener. And centralized the object handling into one system

If this enhancement will not be used often, can it be worked around with a few lines of script?:
Is there a reason why this should be core and not an add-on in the asset library?:
This could not be a small system. I have trying to made it myself right now. But I wish godot would accept this into official engine. So I don't need to reinvent the wheel or using the most efficient route godot could provide internally, instead of me trying to hack things around inefficiently

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
There would be some system that need to implement to work together

Centralize object handler

Not sure this should be put in Module or Window or godot's engine internal object. But I wish godot would initial one map that would be a collection to handle any value and object we need to use in godot side (from now on I would use C# as it was my preferred language)

public class Handle
{
	static Handle()
	{
		JavaScript.Eval("window.GodotJSInteropMap = {}");
	}

	public readonly string ID;
	public Handle(string id)
	{
		ID = id;
	}

	public void SetValue(string code)
	{
		JavaScript.Eval("window.GodotJSInteropMap[" + ID + "] = " + code);
	}
	public object GetValue(string code)
	{
		string eval = "window.GodotJSInteropMap[" + ID + "]" + code;
		var value = JavaScript.Eval($@"
var val = window.GodotJSInteropMap[{ID}].{code};
if(val == null)
  return null;
var type = typeof val;
if(type == \"Number\" || type == \"Boolean\")
  return val;

if(type == \"String\")
  return JSON.stringify(val);

var id = GenerateUniqueID();
window.GodotJSInteropMap[id] = val;
return JSON.stringify({ ID : id });
");
		if(value == null || !(value is string text))
			return value;

		var jtoken = JToken.Parse(text);
		if(jtoken is JValue jv)
			return jv.Value;

		// should be object with ID
		return new Handle(value["ID"].ToString());
	}

	~Handle()
	{
		JavaScript.Eval("delete window.GodotJSInteropMap[" + ID + "]");
	}
}

If godot would incorporate Handle object like this officially, JavaScript.Eval itself might be able to return Handle object without my complicate parsing

Eval injection script

With centralize object handler above, we then could reference js object with handle in godot side. So we should be able to have eval with complex referencing

So I would like to propose an overload method for JavaScript.Eval to that receive custom lookup callback and could write javascript with injection marker

Suppose the injection marker is %%name%%

JavaScript.Eval($@"
	console.log(%%pi%%); // marker name `pi`
	console.log(%%sometext%%.length); // marker name `sometext`, should be string
	console.log(%%someaction%%(\"Run Action Here\"));
",(key) => {
	switch(key)
	{
		case "pi":
			return Math.PI;
		case "sometext":
			return "Random text just for sending string";
		case "someaction":
			return Handle.Eval("alert"); // eval that could return handle as above
		default:
			return null;
	}
});

This new function is just 2 step parser. In will inject a value in the place of marker, if value is handle it will inject window.GodotJSInteropMap[idOfHandle]. And then it would run a normal JavaScript.Eval after that

From above example it would be the same as

JavaScript.Eval($@"
	window.GodotJSInteropMap[\"UniqueIDGeneratedForAlert\"] = alert;
");
JavaScript.Eval($@"
	console.log(3.141592653589793);
	console.log(\"Random text just for sending string\".length);
	console.log(window.GodotJSInteropMap[\"UniqueIDGeneratedForAlert\"](\"Run Action Here\")); // cache alert from above so just emit alert normally
");

With these system we could eval async function smoothly

var someCallBackHandler = Handle.Eval("alert");

JavaScript.Eval($@"
    thirdPartyPromise.then((data) => %%_%%(data));
",(key) => someCallBackHandler);

Anything else?

I think this should cover many use case we normally used with js eval code but there would be much more. Anyone have any thought about this?

BTW, Currently now my most important blockage issue is ability to call C# code from JS side

Even just ability to call static function is lacking

@Calinou
Copy link
Member

Calinou commented Oct 25, 2020

See also #286.

@Thaina
Copy link
Author

Thaina commented Oct 25, 2020

@Calinou Thank you but I have been asked in that issue and still cannot find a solution, do we have anyway we could workaround to directly calling any C# code from js side? I have try to investigate myself and found out that godot export has dynCall defined like emscripten. Do we capable to use it to call C# code in any way at all?

@Calinou Calinou changed the title [Discussion] About js interop Improve interoperability with JavaScript Jan 23, 2021
@Thaina
Copy link
Author

Thaina commented Feb 9, 2021

Part of this might be unnecessary if there are support for https://github.com/WebAssembly/reference-types

@Calinou
Copy link
Member

Calinou commented Dec 25, 2021

Closing, as a JavaScript interface is now implemented in master and 3.4.

@Calinou Calinou closed this as completed Dec 25, 2021
@Calinou Calinou added this to the 3.x milestone Dec 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants