tmedwards / sugarcube-2

SugarCube is a free (gratis and libre) story format for Twine/Twee.
https://www.motoslave.net/sugarcube/2/
BSD 2-Clause "Simplified" License
177 stars 41 forks source link

Running code in SugarCube's scope from an external script. #102

Open ChapelR opened 3 years ago

ChapelR commented 3 years ago

Is your feature request related to a problem? I think importScript() and Tweego's module/head options make it really easy to add JavaScript to games while keeping out of player's ways (e.g., filling up their Story JS) but it has the limitation of all the code being run out of context. It would be nice to be able to run code in context. For example, if I could create a "wrapped" version of my macros that can be included via importScripts().

I envision say, creating "wrapped" versions of my macros that users can add via Tweego modules or via importScripts() to clean up their projects a bit.

Describe the solution you'd like. From the brain-splintering conversation we had on Discord with Cy, the solution we came to was the following wrapper:

SugarCube.evalJavaScript((function () {
    // code to run in-scope goes here
}).toString());

This does work, however, I'm always nervous about relying on SugarCube.* methods given they are typically debug features and undocumented, so is this safe for production code? Is there a better way to do this? Would you rather add a specific method just for this so you can add more sanity checks and such?

cyrusfirheir commented 3 years ago

Doesn't have to be a SugarCube.* function though... Could very well be a function on window, and SC could do the evalJavaScript().

HiEv commented 3 years ago

Yeah, I'd like to see this as well.

Also, if the function calls from HTML element event attributes (e.g. <a onclick="JavaScipt">) could be in scope (or at least have a clearly documented way to work with SugarCube out of scope) then that would solve a lot of grief and headaches for developers who don't yet understand how scope works or that it would be out of scope to work that way.

greyelf commented 3 years ago

Instead of opening SugarCube's engine to external access would adding a "startup" time related event that a external module can listen for (with a relevant reference) be enough to help the module add what it needs to SugarCube's scope?

ChapelR commented 3 years ago

Instead of opening SugarCube's engine to external access would adding a "startup" time related event that a external module can listen for (with a relevant reference) be enough to help the module add what it needs to SugarCube's scope?

I'm trying to envision how this would work. The event would have access to various APIs via it's context? Or would it expose a function like the one suggested? Either way, if there are significant concerns about access, I'm not sure limiting the time that access is granted does much to protect anything, so I guess I don't see the point. It seems like we're limiting access without making any gains.

greyelf commented 3 years ago

In my (limited) experience the time when access (to SugarCube internals) is generally required by an external module is during startup, so the Module has a chance to 'register' itself in some way within the SugarCube scope. eg. initialise variables, define setup object references, etc...

After which such 'registered' references to the Module can be used by code that is run within the SugarCube scope.