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

Can't require Phaser in Electron #3598

Closed
tgrajewski opened this issue Apr 19, 2018 · 24 comments
Closed

Can't require Phaser in Electron #3598

tgrajewski opened this issue Apr 19, 2018 · 24 comments

Comments

@tgrajewski
Copy link

tgrajewski commented Apr 19, 2018

I'm not being able to require Phaser in a desktop application running in Electron v1.8.4.

According to the readme file, one's could install Phaser 3 using NPM like this:

npm install phaser

After that, when running Electron and trying to require Phaser, an error is thrown:

Uncaught ReferenceError: WEBGL_RENDERER is not defined

This error is thrown in the SpriteRender module.

Is Webpack required here and some build steps not explained in the readme? Would be great if it worked as is in Electron, without any additional build steps and requirements if possible.

Phaser v3.5.1, Electron v1.8.4.

@photonstorm
Copy link
Collaborator

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

I was afraid that would be the answer here.

It seems that there are two things in that Webpack config that could be simplified and allowing to skip Webpack completely when using Electron.

One is to allow requiring shader files. This could be done like this:

var fs = require('fs');

require.extensions['.frag'] = function (module, filename) {
    module.exports = fs.readFileSync(filename, 'utf8');
};

...

var shader = require("./shader.frag");

And the other thing would be to set WEBGL_RENDERER and CANVAS_RENDERER being requireable or defined as global true.

Is there anything else preventing Phaser to run in Electron as is?

@photonstorm
Copy link
Collaborator

As long as it doesn't break our build process I'll be happy to take a PR for this.

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

I'll try, please be patient :)

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

@photonstorm I've investigated the situation and it appears, that there are no shortcuts here and both WEBGL_RENDERER and CANVAS_RENDERER would need to be turned into regular constants and maybe put inside the const module:

var CONST = {
    _WEBGL_RENDERER: WEBGL_RENDERER,
    _CANVAS_RENDERER: CANVAS_RENDERER,
    ...

And then everywhere they are used of course we would need to have to import (require) them like you do already with other constants:

var CONST = require('./const');

if (CONST._WEBGL_RENDERER && CONST._CANVAS_RENDERER)
    ...

That would require around 20 files to be modified. Would you accept such PR, or you have a different idea how to approach this?

@photonstorm
Copy link
Collaborator

The problem is, if you do that, both the Canvas and WebGL files will be included in to the bundle. That's why we use the webpack plugin to handle it, because they're parsed out at build time and only the relevant renderer (or both, depending on the config) is included.

Swap them for normal consts and it'll always include them both :(

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

That complicates things a bit. I'm not a Webpack user, but maybe that would just work in the Webpack config file:

plugins: [
    new webpack.DefinePlugin({
        'CONST._CANVAS_RENDERER': JSON.stringify(true),
        'CONST._WEBGL_RENDERER': JSON.stringify(true)
    }),
],

I'll experiment with that and maybe more ideas will popup, will let you know.

@photonstorm
Copy link
Collaborator

Possibly. Will test it now and see.

@photonstorm
Copy link
Collaborator

Damn, sadly not. Swapping to use CONST.CANVAS_RENDERER (and using that in the source) seems to make it get skipped by DefinePlugin, as the bundle contains:

if (CONST.WEBGL_RENDERER)
{
    renderWebGL = __webpack_require__(/*! ./ImageWebGLRenderer */ "./gameobjects/image/ImageWebGLRenderer.js");
}

if (CONST.CANVAS_RENDERER)
{
    renderCanvas = __webpack_require__(/*! ./ImageCanvasRenderer */ "./gameobjects/image/ImageCanvasRenderer.js");
}

Where-as if the plugin had run properly it would contain:

if (true)
{
    renderWebGL = __webpack_require__(/*! ./ImageWebGLRenderer */ "./gameobjects/image/ImageWebGLRenderer.js");
}

if (true)
{
    renderCanvas = __webpack_require__(/*! ./ImageCanvasRenderer */ "./gameobjects/image/ImageCanvasRenderer.js");
}

@photonstorm
Copy link
Collaborator

Looking at the DefinePlugin docs (https://webpack.js.org/plugins/define-plugin/) it looks like we could store the flags inside the node environment process instead, Not really sure if that makes it any easier for non-Webpack users though.

@photonstorm
Copy link
Collaborator

Yeah that works: process.env.WEBGL_RENDERER and it gets compiled out properly. Any use to Electron?

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

Yes, I believe it should work, that would of course require to define these environment variables before importing Phaser.

I've also found another way from the DefinePlugin's documentation, by leveraging the typeof keyword we could have this:

    new webpack.DefinePlugin({
        "typeof CANVAS_RENDERER": JSON.stringify(true),
        "typeof WEBGL_RENDERER": JSON.stringify(true)
    }),

And it seems to work too, with code like this:

if (typeof WEBGL_RENDERER && typeof CANVAS_RENDERER)

Of course that would require to prefix all WEBGL_RENDERER and CANVAS_RENDERER occurences with typeof.

I don't actually understand what the typeof variant changes here, if anything. But it just makes the code run in Electron and Node.js because the expressions are now valid.

@tgrajewski
Copy link
Author

@photonstorm So, I can make a PR with typeof's or you can modify the code yourself, because it seems to be fast find and replace (if you accept such solution). Your decision here. It won't yet make it run in Electron, but I think this was the biggest obstacle.

@photonstorm
Copy link
Collaborator

I don't mind sticking the typeof checks in as they are only used when including the render files, it won't make any difference to the runtime speed if built via Webpack, and I doubt if ingested via any other means either.

Doesn't get around the shader raw-loader use though.

@tgrajewski
Copy link
Author

tgrajewski commented Apr 19, 2018

For shader loading we can leverage the code from my earlier comment and it will work fine.

This needs to be put in somewhere at the beginning of the Phaser's code, probably in src/phaser.js:

var fs = require('fs');
var loadAsString = function (module, filename)
{
    module.exports = fs.readFileSync(filename, 'utf8');
}

require.extensions['.frag'] = loadAsString; 
require.extensions['.vert'] = loadAsString; 

@photonstorm
Copy link
Collaborator

Where does this part go?

var fs = require('fs');

require.extensions['.frag'] = function (module, filename) {
    module.exports = fs.readFileSync(filename, 'utf8');
};

As it can't go in the Phaser source.

@tgrajewski
Copy link
Author

It would need to be in Phaser's source, somewhere at the beginning, before first requires. It could be completely disabled when building with Webpack using the same trick with the DefinePlugin (it would be always stripped with Webpack).

@photonstorm
Copy link
Collaborator

Ahh ok - it'd still try and bundle in the whole of fs for any other packager though, which seems a tad much, but better than not working at all I guess.

@photonstorm
Copy link
Collaborator

Can require.extensions take multiple extensions? As it needs to cover both .frag and .vert files. Perhaps: require.extensions['.frag', '.vert'] ?

@tgrajewski
Copy link
Author

tgrajewski commented Apr 20, 2018

@photonstorm It's done, that's awesome, thank you very much!

@photonstorm
Copy link
Collaborator

I'm going to have to undo the require.extensions change because it breaks packaging Phaser with Parcel. I'll keep the typeof change in though. Will try and find a new workflow for the shaders for the next release to avoid needing raw-loader or fs.

@photonstorm
Copy link
Collaborator

Ok all sorted :) 3.7.1 includes a new way of handling shaders, so Electron should work fine now.

@tgrajewski
Copy link
Author

I was bit worried for a moment, but great you've managed this. Desktop games in Phaser, yay! :)

@honkskillet
Copy link

Can you recommend the best way to scaffold out a Phaser + Electron project?

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

3 participants