I’m following this tutorial to start building a multiplayer game with express
and jsdom
. My game runs locally in VSCode’s Live Server. However, when I launch node --inspect server/index.js
, I get the following error (from Chrome DevTools):
Uncaught TypeError: Cannot read property 'gl' of null
I’ve tried Phaser
versions 3.15.1, 3.24.1, 3.50.0 and 3.50.1, and node
versions 15.y.z and 10.13.0 – all give the same error (the tutorial uses 3.15.1 and 10.13.0). I’m on MacOS 10.15.7.
Any idea?
Here’s my simplified code. I realise it’s a lot of code, and I apologise. I really appreciate any hints you can give me.
server/index.js
const path = require('path');
const jsdom = require('jsdom');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const { JSDOM } = jsdom;
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
server.listen(8081, function () {
console.log(`Listening on ${server.address().port}`);
});
function setupAuthoritativePhaser() {
JSDOM.fromFile(path.join(__dirname, 'authoritative_server/index.html'), {
runScripts: 'dangerously',
// ^ allows JSDOM to run scripts in the html file
resources: 'usable',
// ^ allows JSDOM to load external resources
pretendToBeVisual: true,
// ^ makes JSDOM behave like a visual browser
});
}
setupAuthoritativePhaser();
server/authoritative_server/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Sharing Slime</title>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.js"></script>
<script src="https://cdn.jsdelivr.net/npm/phaser-matter-collision-plugin/dist/phaser-matter-collision-plugin.js"></script>
<style type="text/css">
body { margin: 0; }
</style>
</head>
<body>
<script src="js/game.js"></script>
</body>
</html>
server/authoritative_server/js/game.js
const config = {
type: Phaser.HEADLESS,
autoFocus: false,
width: 400,
height: 240,
pixelArt: true,
zoom: 2,
physics: {
default: 'matter',
matter: {
gravity: {y: 1.5},
}
},
// Add plugins
plugins: {
scene: [
{
phaser-matter-collision-plugin
plugin: PhaserMatterCollisionPlugin,
key: 'matterCollision',
mapping: 'matterCollision'
}
]
},
scene: {
preload() {
/* Commented stuff -- still raises error */
},
create() {
console.log("Running AUTHORITATIVE");
/* Commented stuff -- still raises error */
},
update() {
},
}
};
const game = new Phaser.Game(config);
(The files below run locally in VSCode’s Live Server)
server/public/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Sharing Slime</title>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.js"></script>
<script src="https://cdn.jsdelivr.net/npm/phaser-matter-collision-plugin/dist/phaser-matter-collision-plugin.js"></script>
<style type="text/css">
body { margin: 0; }
</style>
</head>
<body>
<!-- <script src="/socket.io/socket.io.js"></script> -->
<script type="module" src="js/game.js"></script>
</body>
</html>
server/public/js/game.js
import Player from "./player.js";
const config = {
type: Phaser.AUTO,
parent: 'sharing-slime',
width: 400,
height: 240,
pixelArt: true,
zoom: 2,
physics: {
default: 'matter',
matter: {
gravity: {y: 1.5},
// debug: true,
}
},
// Add plugins
plugins: {
scene: [
{
plugin: PhaserMatterCollisionPlugin,
key: 'matterCollision',
mapping: 'matterCollision'
}
]
},
scene: {
preload() {
// Load tileset in PNG and JSON form
this.load.image('base_tiles', 'assets/base_tiles.png');
this.load.tilemapTiledJSON('tilemap', 'assets/v1.2.2/tilemap.json');
// Load players' sprite sheet
this.load.spritesheet('red', 'assets/red.png', {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet('blue', 'assets/blue.png', {
frameWidth: 32,
frameHeight: 32,
});
// Load other sprites
this.load.image('bomb', 'assets/bomb.png');
},
create() {
// this.socket = io();
console.log("Running PUBLIC");
this.keys = this.input.keyboard.createCursorKeys();
this.matter.world.setBounds(0, 0, game.config.width, game.config.height, 200);
// Create map
const map = this.make.tilemap({key: 'tilemap'});
// Add the tileset image we are using
const tileset = map.addTilesetImage('standard_tiles', 'base_tiles', 16, 16, 2, 2);
// Create remaining layer, in order
const skyLayer = map.createDynamicLayer('Sky', tileset, 0, 0);
const platformLayer = map.createDynamicLayer('Platform', tileset, 0, 0);
const groundLayer = map.createDynamicLayer('Ground', tileset, 0, 0);
// Enable collisions with layers
groundLayer.setCollisionByProperty({collides: true});
platformLayer.setCollisionByProperty({collides: true});
this.matter.world.convertTilemapLayer(groundLayer);
this.matter.world.convertTilemapLayer(platformLayer);
// Visualise all the matter bodies
// this.matter.world.createDebugGraphic();
// Create player 1 (red)
// const { x, y } = map.findObject("spawn_one", obj => obj.name === "Spawn Point");
this.playerRed = new Player(this, 80, 100, 'red');
this.playerRed.direction = 'right';
// Create player 2 (blue)
this.playerBlue = new Player(this, 280, 100, 'blue');
this.playerBlue.direction = 'left';
// Create bomb(s)
this.bomb = this.matter.add.sprite(50, 32, 'bomb', 0);
this.bomb.setBody({
type: 'polygon',
sides: 6,
radius: 8
});
this.bomb.ignoreGravity = true;
this.bomb.setBounce(1);
this.bomb.setVelocity(Phaser.Math.Between(-5, 5), 3);
this.bomb.setFriction(0, 0, 0);
},
update() {
},
}
};
var game = new Phaser.Game(config);