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

Ten general questions about making a Celeste mod with V8 integration #35

Open
iSkLz opened this issue Aug 26, 2020 · 0 comments
Open

Ten general questions about making a Celeste mod with V8 integration #35

iSkLz opened this issue Aug 26, 2020 · 0 comments

Comments

@iSkLz
Copy link

iSkLz commented Aug 26, 2020

Hello,

So I'm making a Celeste mod using Everest that allows use of scripts, this makes it possible for people who have never coded before (the Everest mod loader allows for loading maps and not just code mods, so some modders don't code) to code some simple behaviours using the simple, easy and well-documented JavaScript language, rather than having to deal with how complex C# is for beginners.

I have a BUNCH of questions about using V8.NET for that purpose, since I couldn't understand much from the wiki, which are as follows:
(Please note that I have set up an "initialization" method that runs on a seperate thread and does some preparations while the game is loading to speed things up, so if any required process requires or can benefit from beforehand preparations please let me know in the answer, thank you)

First question:
TLDR: What is the fastest way to expose a CLR object or type "as is" from the game or from an other mod that I cannot edit or change?
Speed is the most important thing here, since its a game.
While writing this question I noticed that creating a binding and putting it as a property on the global object works nicely (I thought it doesn't), but its unpredictably unreliable. Some classes just don't work for seemingly no reason, like System.Diagnostics.Stopwatch, if I try doing this:
Stopwatch timer = new Stopwatch();
Then this:
Engine.GlobalObject.SetProperty("timer", timer);
I would get "undefined" whenever I try to access any method or property timer.xxxx from inside a script. The XML docs says stuff about sub-object members and boxed primitives, I don't understand what that means, I would assume its boxed value-types but not sure, am I missing something here?
I then tried writing an "ObjectExporter" class that takes an object, and using some Reflection it exposes the properties and methods of the original object. That worked very very well, except for the fact that its SO DAMN SLOW both on initialization and on execution, it horribly slows down the game's loop.
I saw the class ObjectBinder and thought of deriving from it but I couldn't figure out how, and I didn't know if it'll work for any class or it's specific.

Second question:
What is the fastest way to expose a CLR object or type that I have control over?
And by fastest, I mean which approach would give me the best performance on calling methods and retrieving properties.
I need to write some helper objects and classes for the scripts to use and I want those to work the FASTEST possible, since those are gonna be used very very frequently inside scripts, they're basically the main link between the scripts and the game.
The current method I'm using is the regular "binding -> property on global object" method, which works well but doesn't feel like the best approach, XML docs indicate that creating bindings is a slow process which is fine since again, speed doesn't matter since all the initialization work is done on a seperate thread, but I have no idea if it executes the fastest or not.

Third question:
Say, I did expose a CLR object to the script, what is the best way to also expose extensions methods alongside the object? (If that's possible at all, even if it means using slow means like Reflection-ing every single static type to look for the extensions, since as I mentioned earlier, initialization doesn't have to be fast)
Extensions would make it much easier for me and other mods to provide extra functionality for scripts on top of the game's original classes that's used in a very simple fashion, avoiding having to write either a messy mass of methods on a single class, or expose the mass of static classes containing extensions.

Fourth question:
How does one use a generic method or a generic type from inside a script? (If its possible ofc)

Fifth question:
Having exposed a type, is it possible to use static methods and fields/properties? If so, should I be exposing a TypeBinder or a regular plain Type? And if extension methods are possible to expose, what can I do (on a method that extends System.Type) to expose a new object instance given a Type object?

Sixth question:
What's with the context? What's its use? What I know about V8 is that the context represents quite literally the context for the scripts, but it feels a little bit "ambigous" -would you say- to use.
Edit: While writing this question I realized that contexts can be used to provide a custom object template for the GlobalObject property, is that really the only use to it?

Seventh question:
In the context of contexts, because I feel this is related to contexts, does a globally-scoped script-defined function or variable persist across multiple script executions (including the case where executed scripts are different)? If so, does that mean I have to "new" a V8Engine for each mod to not cause global conflictions? If that's the case, is it possible to do some sort of "clean-up" to remove all global variables and functions? That would be useful for scripters.

It might seem like I don't like that but I actually do, it means I can initialize a seperate engine for each mod and execute a mod-defined "Initialize.js" script that imports the types and classes (if there is a good answer to question 1) and puts them on global variables, and defined some global functions for use in the other scripts, that's a huge speed boost to executing the scripts.

Eighth question:
Is there a good way to use generator functions? I built an Iterator class that takes the InternalHandle returned from a generator function and iterates through it, but its a bit slow since it has to call next() on the iterator on each iteration and it also has to retrieve "current" and "done" properties from the returned object of next() which is quite a bit of work, even when I changed from using Handle to using InternalHandle (since its a class I can dispose of the handles in the deconstructor) which sped things up quite a bit but still not enough to support a descent amount of code inside the actual game.

Ninth question:
What is the fastest way to retrieve a V8NativeObject out of an InternalHandle representing a JavaScript object? I tried InternalHandle.Object but that is null all the time, and the only approach I found to be working is using InternalHandle.Engine.CreateObject as I did it on an extension method, which seems slow and since it's used inside the Iterator class I built (which I really need to speed up) a speed boost on this could help.

Tenth question: (and possibly the most important question)
Debugging, debugging, DEBUGGING. Looking at the source code I found a line that calls some method on the proxy with parameters:
bool enableDebugging, void* debugMessageDispatcher, int debuggerPort
I tried changing those (well, I couldn't change the code and build since I was offline and didn't have some required stuff on Visual Studio so I had to do a little bit of runtime IL manipulation which I'm pretty sure works just as well, and its a temporary solution anyways) and it doesn't work, or at least it doesn't with Chrome's Node.JS dedicated-debugger (which works nicely with ClearScript, so my big brain told me it should work with anything V8). The only theory I have in mind is that the two use different debugging protocol, I remember reading a document about a legacy debugging protocol and a new one. If that's the case, is it planned to implement the new one? I would've done it myself if I wasn't braindead. Debugging would be a very major plus to the library and would make scripting much easier.

Those are all my questions, and I'm ready to write a small FAQ page based on the answers, that is if there's gonna be any answers at all and if it's gonna be useful.
Sorry if there are too many questions, or they're a little bit too long. I didn't want to post TEN issues seperately for each question repeating how I'm making a Celeste mod and everything, and I wanted to make my questions as specific as possible to avoid confusion.

And finally, here are some puns as my way of showing gratitude for the effort that's gonna be needed to put an answer altogether for the questions, or to read them at all :)

  1. What does the gardener do when he finishes his work? He leafs and goes home.
  2. I was asked to put the cat out, I didn't know it had caugh fire.
  3. I have a builder friend, I asked him to give me a pun but he said he's "working" on one.
  4. What do you say about a very visually-appealing hand-drawn painting? It "draws" attention.
  5. I once heard a pun about paper, it was "tearable".
  6. My friend was offered a job as a stand-up comedian, but he prefered "sitting".
  7. Mettaton: Have you met any other robots like me, human?
    Human: Yup, I've met-a-ton.
  8. How does one describe long hands? "Catchy".

Thanks in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant