Hello everyone
I'm trying to create a multiplayer game in phaser. Once the game has been created i have two player. When i move my player (green) I can see the movement in a new page of my browser (not green).

Everything works for the best. But considering that the number of movements is high, after some time you get this error:
VM2455:300 Uncaught DOMException: Failed to construct 'RTCPeerConnection': Cannot create so many PeerConnections
at new WrappedRTCPeerConnection (<anonymous>:300:28)
at Object.m._startPeerConnection (http://localhost/SP2P/lib/peer.js:1:33131)
at Object.m._getPeerConnection (http://localhost/SP2P/lib/peer.js:1:32849)
at Object.m.startConnection (http://localhost/SP2P/lib/peer.js:1:32216)
at new d (http://localhost/SP2P/lib/peer.js:1:2579)
at p.connect (http://localhost/SP2P/lib/peer.js:1:25072)
at PeerClient.conn (http://localhost/SP2P/P2P/PeerClient.js:100:27)
at Object.P2PMaze.send (http://localhost/SP2P/js/GameMultiplayer.js:42:29)
at P2PMaze.GameMultiplayer.update (http://localhost/SP2P/js/GameMultiplayer.js:236:21)
at Phaser.StateManager.update (http://localhost/SP2P/lib/phaser.js:31183:39)
Peerjs Version: 0.3.9
Chrome version: 67.0.3396.62
It seems like you are creating a lot of new connections. Could you show your code?
Also it would be helpful if you use the unminified version of the library, you have it on dist/peer.js
Also it looks like you are using an old peerjs version, latest npm version is 0.3.16:
https://www.npmjs.com/package/peerjs
Of course. My code is this:
var P2PMaze = P2PMaze || {};
// global variable
var map;
var backgroudLayer;
var blockedLayer;
var player;
var toOpponentPlayer = [];
var cursor;
var items;
var logicalOrder = {};
var playerOrder = 1; // the player starts to 1 for compare the item to take
var createdOpponentPlayer = false;
var opponentPlayer;
var jump = false;
var hitPlatformO;
P2PMaze.send = function(data){
var id = P2PMaze.peer.getConnectTo().getId();
var conn = P2PMaze.peer.conn(id);
P2PMaze.peer.sendData(conn, data);
};
P2PMaze.GameMultiplayer = function(){
console.log("%cStarting GameMultiplayer", "color:black; background:yellow");
};
P2PMaze.GameMultiplayer.prototype = {
preload: function() {
this.game.load.tilemap('temp', 'assets/tilemaps/temp.json', null, Phaser.Tilemap.TILED_JSON);
this.game.load.image('tempImage', 'assets/images/tiles.png');
this.load.spritesheet('player', 'assets/images/dude.png',32,48);
this.load.image('redcup', 'assets/images/estintore_grande.png');
this.load.image('greycup', 'assets/images/greencup.png');
this.load.image('bluecup', 'assets/images/bluecup.png');
},
create: function() {
...
var keyPlayer = {"key": P2PMaze.peer.getId()};
var posx ={"posx":player.position.x};
var posy = {"posy":player.position.y}
var key = {"Key":player.key};
toOpponentPlayer.push(keyPlayer);
toOpponentPlayer.push(posx);
toOpponentPlayer.push(posy);
toOpponentPlayer.push(key);
P2PMaze.send(toOpponentPlayer);
// move player with cursor key
cursor = this.game.input.keyboard.createCursorKeys();
},
update: function(){
// creation opponent player
if(P2PMaze.dataReceived!=undefined && createdOpponentPlayer==false){
opponentPlayer = this.game.add.sprite(P2PMaze.dataReceived[1].posx, P2PMaze.dataReceived[2].posy, 'player');
// create the phisics body. Can be a single object (as in this case) or of array of Objects
this.game.physics.arcade.enable(opponentPlayer);
// Player physics properties. Give the little guy a slight bounce.
opponentPlayer.body.bounce.y = 0.2;
opponentPlayer.body.gravity.y = PLAYER.GRAVITY_Y;
opponentPlayer.body.collideWorldBounds = true;
// see image: 0, 1, 2, 3 is the frame for runring to left
// see image: 5, 6, 7, 8 is the frame for running to right
// 10 = frames per second
// the 'true' param tell the animation to loop
opponentPlayer.animations.add('left', [0, 1, 2, 3], 10, true);
opponentPlayer.animations.add('right', [5, 6, 7, 8], 10, true);
//the camera will follow the player in the world
this.game.camera.follow(opponentPlayer);
// the opponent player start to 4 frame
opponentPlayer.frame = 4;
createdOpponentPlayer=true;
}
// collisio to do
// https://phaser.io/docs/2.4.4/Phaser.Physics.Arcade.html#collide
var hitPlatform;
hitPlatform = this.game.physics.arcade.collide(player, blockedLayer);
if(opponentPlayer!=undefined){
hitPlatformO = this.game.physics.arcade.collide(opponentPlayer, blockedLayer);
}
// player movement
// NB: comment these to gain less control over the sprite
// player.body.velocity.y = 0;
player.body.velocity.x = PLAYER.VELOCITY_X_START;
if(opponentPlayer!=undefined){
opponentPlayer.body.velocity.x = PLAYER.VELOCITY_X_START;
}
if(this.game.input.activePointer.justPressed()){
// move on the direction of the input
this.game.physics.arcade.moveToPointer(player, 150);
// player.animations.play('left');
}
var updatePos = [];
if(cursor.left.isDown){
player.body.velocity.x = PLAYER.VELOCITY_X_LEFT;
player.animations.play('left');
// send to opponent player the left position of player
var keyupdating = {"key": "left"};
var updateX ={"updatePosx":player.x};
var updateY ={"updatePosy":player.y};
updatePos.push(keyupdating);
updatePos.push(updateX);
updatePos.push(updateY);
// SEND
P2PMaze.send(updatePos);
}else if(cursor.right.isDown){
player.body.velocity.x = PLAYER.VELOCITY_X_RIGHT;
player.animations.play('right');
var keyupdating = {"key": "right"};
var updateX ={"updatePosx":player.x};
var updateY ={"updatePosy":player.y};
updatePos.push(keyupdating);
updatePos.push(updateX);
updatePos.push(updateY);
P2PMaze.send(updatePos);
}
else{
// Stand still
player.animations.stop();
player.frame = 4;
}
// if(cursor.up.isDown && player.body.touching.down){
if(cursor.up.isDown && hitPlatform){
player.body.velocity.y = -250;
}
// move the opponent player to specific LEFT position
if(opponentPlayer!=undefined && P2PMaze.dataReceived[0].key=="left"){
var posx = P2PMaze.dataReceived[1].updatePosx;
var posy = P2PMaze.dataReceived[2].updatePosy;
// when the opponent player came to specific position + or - 1 (for avoid loop), stop the animation and set the frame to 4
if(Math.floor(opponentPlayer.x) === Math.floor(posx) ||
Math.floor(opponentPlayer.x) === (Math.floor(posx) +1) ||
Math.floor(opponentPlayer.x) === (Math.floor(posx) -1))
{
opponentPlayer.animations.stop();
opponentPlayer.frame = 4;
}else {
this.game.physics.arcade.moveToXY(opponentPlayer,Math.floor(posx),Math.floor(posy));
opponentPlayer.animations.play('left');
}
}
// move the opponent player to specific RIGHT position
if(opponentPlayer!=undefined && P2PMaze.dataReceived[0].key=="right"){
var posx = P2PMaze.dataReceived[1].updatePosx;
var posy = P2PMaze.dataReceived[2].updatePosy;
// when the opponent player came to specific position + or - 1 (for avoid loop), stop the animation and set the frame to 4
if(Math.floor(opponentPlayer.x) === Math.floor(posx) ||
Math.floor(opponentPlayer.x) === (Math.floor(posx) +1) ||
Math.floor(opponentPlayer.x) === (Math.floor(posx) -1))
{
opponentPlayer.animations.stop();
opponentPlayer.frame = 4;
}else {
this.game.physics.arcade.moveToXY(opponentPlayer,Math.floor(posx),Math.floor(posy));
opponentPlayer.animations.play('right');
}
}
// Checks for overlaps between two game objects.
// - The first object or array of objects to check.
// - The second object or array of objects to check.
// - An optional callback function that is called if the objects overlap.
// The two objects will be passed to this function in the same order in which you specified them,
// unless you are checking Group vs. Sprite, in which case Sprite will always be the first parameter.
// - A callback function that lets you perform additional checks against the two objects if
// they overlap. If this is set then overlapCallback will only be called if
// this callback returns true
// - The context in which to run the callbacks.
this.game.physics.arcade.overlap(player, items, this.collect, this.choiceItems, this);
if(opponentPlayer!=undefined){
this.game.physics.arcade.overlap(opponentPlayer, items, this.collect, this.choiceItems, this);
}
},
...
I try to create a single connection. I create a global variable var conn = undefined and then assign the the connection in the update:
if(P2PMaze.dataReceived!=undefined && createdOpponentPlayer==false){
opponentPlayer = this.game.add.sprite(P2PMaze.dataReceived[1].posx, P2PMaze.dataReceived[2].posy, 'player');
....
var id = P2PMaze.peer.getConnectTo().getId();
conn = P2PMaze.peer.conn(id);
createdOpponentPlayer=true;
}
and
...
...
var updatePos = [];
if(cursor.left.isDown){
player.body.velocity.x = PLAYER.VELOCITY_X_LEFT;
player.animations.play('left');
// send to opponent player the left position of player
if(conn!=undefined){
var keyupdating = {"key": "left"};
var updateX ={"updatePosx":player.x};
var updateY ={"updatePosy":player.y};
updatePos.push(keyupdating);
updatePos.push(updateX);
updatePos.push(updateY);
P2PMaze.send(updatePos);
}
}else if(cursor.right.isDown){
player.body.velocity.x = PLAYER.VELOCITY_X_RIGHT;
player.animations.play('right');
// send to opponent player the right position of player
if(conn!=undefined){
var keyupdating = {"key": "right"};
var updateX ={"updatePosx":player.x};
var updateY ={"updatePosy":player.y};
updatePos.push(keyupdating);
updatePos.push(updateX);
updatePos.push(updateY);
P2PMaze.send(updatePos);
}
}
....
But in this way my multiplayer doesn't work. For this I thought that the correct approch is
var id = P2PMaze.peer.getConnectTo().getId();
var conn = P2PMaze.peer.conn(id);
P2PMaze.peer.sendData(conn, data);
P2PMaze.peer is assignment that i doing in a form.
I'm using the min version that i have inside my lib dir.
If I'm not wrong, here:
P2PMaze.send = function(data){
var id = P2PMaze.peer.getConnectTo().getId();
var conn = P2PMaze.peer.conn(id);
P2PMaze.peer.sendData(conn, data);
};
You are opening a new connection every time you want to send a message. You should save your created connection somewhere, and use it for all messages, for example, something like this:
P2PMaze.connect = function(){
var id = P2PMaze.peer.getConnectTo().getId();
P2PMaze.otherPlayer = P2PMaze.peer.conn(id);
};
P2PMaze.send = function(data){
P2PMaze.otherPlayer.send(data);
}
Use the send method from the connection object:
https://peerjs.com/docs/#dataconnection-send
And listen for the data event on the connection:
https://peerjs.com/docs/#dataconnection-on-data
Maybe I didn't understood your code. I think conn is peerjs, and sendData is send from connection Object? if not, please post the code of those functions too.
conn and sendData are two methods of my class. :
`
/**
* Class that create a peer. The param are:
* @param {string} id the id of my peer
* @param {string} host the path of server
* @param {int} port the number of port
* @param {path} pht the app name of the server. It is useful for establish the connection
*/
class PeerClient {
constructor(id, h, p, pth){
this._id = id;
this._host = h;
this._port = p;
this._path = pth;
this._peerToConnect = undefined;
this._peer = new Peer(this._id, {
host: this._host, // 'localhost',
port: this._port, // 9000,
path: this._path // '/peerjs'
});
}
/**
* Return the id of my peer
*/
getId() {
return this._id;
}
/**
* Return the peer connected with me
*/
getConnectTo() {
return this._peerToConnect;
}
/**
* This metohd is used for setting the peer with i want connect. It used when I receive the request of
* connection by a specific peer.
* @param {peer} peerToConnect
*/
setConnectTo(peerToConnect){
this._peerToConnect = new PeerClient(peerToConnect, this._host, this._port, this._path);
}
/**
* This allow to create the player
* @param {*} x initial position x
* @param {*} y initial position y
* @param {*} identifierString the unique string by which we'll identify the image later in our code.
*/
createPlayer(x,y, identifierString){
return P2PMaze.game.add.sprite(x,y,identifierString)
}
/**
* Make the peer avilable for the connection
*/
openConnection() {
this._peer.on('open', function(id_peer) {
console.log('My peer ID is: ' + id_peer); //DEBUG
});
}
/**
* Closes the data connection gracefully, cleaning up underlying DataChannels and PeerConnections.
* REF:https://stackoverflow.com/questions/25797345/peerjs-manually-close-the-connection-between-peers
* @param {object} conn i
*/
closeConnection(conn) {
conn.on('open', function(){
conn.close();
alert("connection close");
});
}
/**
* See the error of peer . * .
*/
seeError(){
this._peer.on('error', function(err){
alert(err.message);
});
}
/**
* This method is used for create a connection
* @param {object} id_another_peer is the id of peer that I want to connect
*/
conn(id_another_peer) {
return this._peer.connect(id_another_peer);
}
/**
* Sharing data among peer. The first param is the value that return from the previusly method (conn)
* @param {object} conn this is the connection
* @param {object} data this is the data to send
*/
sendData(conn, data) {
conn.on('open', function(){
conn.send(data);
});
}
/**
* This method is used for receive data.
* @param {method} callback return the data that arrived from sender
*/
enableReceptionData(callback) {
this._peer.on('connection', function(conn) {
conn.on('data', function(data){
console.log("--------------------------------");
console.log("MESSAGE RECEIVED : \n");
console.log(data);
console.log("--------------------------------");
callback(data);
});
});
}
}`
I have tried with this solution that you suggest me but it doesn't work.
In other words: if i create multiple connections then it work. If i create a single connection, the first time work (because i can see the creation og otherpeer) but he stop to send data. I don't know why.
I'm using the latest (I think) the latest version of peerjs. 0.3.9 that the site indicate.
sendData(conn, data) {
conn.on('open', function(){
conn.send(data);
});
}
Here you are creating a new listener, and when the event 'open' come, you send the data. You should just send the data:
sendData(conn, data) {
conn.send(data);
}
The Open event is to check if the connection has been established. You can check for it, or you can just send the data and let it fail if the connection is not still open.
The 'open' event is send once after you open a connection, that's why it is working to you only with multiple conections. Because the second time you want to send data, you will wait indefinitely for the 'open' event, that as I said, it is emitted only one time when the connection is established.
So apply that change I proposed, and create the connection only once.
ooooh very good. Thanks. I did not understand. When i try my code i post the result. thanks thanks thanks.
Ok it works, but now i don't receive the data:
`
/**
* This method is used for receive data.
* @param {method} callback return the data that arrived from sender
*/
enableReceptionData(callback) {
this._peer.on('connection', function(conn) {
conn.on('data', function(data){
console.log("--------------------------------");
console.log("message received : \n");
console.log(data);
console.log("--------------------------------");
callback(data);
});
});
}`
I see from console that the connection and the sharing of data is ok, but this method doesn't work. Can you explain me why ?
Is this._peer the connection object?
Something like this?
this._peer = peer.connect('id')
Also I don't see where are you calling enableReceptionData, keep in mind that if you are testing in your local computer, the connection may be so fast that the 'connection' event is being launched inmediately after you call peer.connect(id). So you should be doing something like:
this._peer = peer.connect('id');
enableReceptionData(callback);
If you do any other thing between the connection creation and the listeners (enableReceptionData), you may be missing the 'connection' event.
If I undestrand the conn.on('open', function(){ .. } can be omitted. But if i print in the console:
`
enableReceptionData(callback) {
console.log(conn); // IMPORTANT
this._peer.on('connection', function(conn) {
conn.on('data', function(data){
console.log("--------------------------------");
console.log("message received : \n");
console.log(data);
console.log("--------------------------------");
callback(data);
});
});
}
`
I retrieve this message:
d聽{_events: {鈥, options: {鈥, open: false, type: "data", peer: "zzz-6UsEonI",聽鈥
you can see the open is false.
My new PeerClass is this:
` /**
* Class that create a peer. The param are:
* @param {string} id the id of my peer
* @param {string} host the path of server
* @param {int} port the number of port
* @param {path} pht the app name of the server. It is useful for establish the connection
*/
class PeerClient {
constructor(id, h, p, pth){
this._id = id;
this._host = h;
this._port = p;
this._path = pth;
this._peerToConnect = undefined;
this._conn = undefined;
this._peer = new Peer(this._id, {
host: this._host, // 'localhost',
port: this._port, // 9000,
path: this._path // '/peerjs'
});
}
/**
* Return the id of my peer
*/
getId() {
return this._id;
}
/**
* Return the peer connected with me
*/
getConnectTo() {
return this._peerToConnect;
}
/**
* This metohd is used for setting the peer with i want connect. It used when I receive the request of
* connection by a specific peer.
* @param {peer} peerToConnect
*/
setConnectTo(peerToConnect){
this._peerToConnect = new PeerClient(peerToConnect, this._host, this._port, this._path);
}
/**
* This allow to create the player
* @param {*} x initial position x
* @param {*} y initial position y
* @param {*} identifierString the unique string by which we'll identify the image later in our code.
*/
createPlayer(x,y, identifierString){
return P2PMaze.game.add.sprite(x,y,identifierString)
}
/**
* Make the peer avilable for the connection
*/
open() {
this._peer.on('open', function(id_peer) {
console.log('My peer ID is: ' + id_peer); //DEBUG
});
}
openConnection(){
this._conn.on('open', function(data){
console.log(data);
});
}
/**
* Closes the data connection gracefully, cleaning up underlying DataChannels and PeerConnections.
* REF:https://stackoverflow.com/questions/25797345/peerjs-manually-close-the-connection-between-peers
* @param {object} conn i
*/
closeConnection(conn) {
conn.on('open', function(){
conn.close();
alert("CONNESSIONE CHIUSA"); // TODO mettere un qualche messaggio
});
}
/**
* See the error of peer . * .
*/
seeError(){
this._peer.on('error', function(err){
alert(err.message);
});
}
/**
* This method is used for create a connection
* @param {object} id_another_peer is the id of peer that I want to connect
*/
conn(id_another_peer) {
this._conn = this._peer.connect(id_another_peer);
return this._conn;
}
/**
* return the connection
*/
getConnection(){
return this._conn;
}
/**
* Sharing data among peer. The first param is the value that return from the previusly method (conn)
* @param {object} conn this is the connection
* @param {object} data this is the data to send
*/
// sendData(data) {
// this._conn.on('open', function(){
// this._conn.send(data);
// });
// }
sendData(data) {
this._conn.send(data);
}
/**
* This method is used for receive data.
* @param {method} callback return the data that arrived from sender
*/
enableReceptionData(callback) {
this._peer.on('connection', function(conn) {
console.log(conn);
conn.on('data', function(data){
console.log("--------------------------------");
console.log("MESSAGE UPDATED : \n");
console.log(data);
console.log("--------------------------------");
callback(data);
});
});
}
}`
When you open the connection in:
conn(id_another_peer) {
this._conn = this._peer.connect(id_another_peer);
return this._conn;
}
You should inmediatly start listening the events, so:
conn(id_another_peer) {
this._conn = this._peer.connect(id_another_peer);
this.enableReceptionData();
return this._conn;
}
Also I'm sorry I miss explained before, the 'open' event is emmited when you connect to the PeerServer, so you must listen for that event before trying to connect to other peers, and it will be thrown after you do new Peer(id):
var peer = new Peer();
peer.on('open', function(id) {
console.log('My peer ID is: ' + id);
// here you have your ID and you can start opening connections to other peers
});
so listen for the 'open' event in your constructor after new Peer() and when you get the 'open' event, you can connect to other peers.
Note that for the 'open' event it is as I explained before, you must listen to it inmediately after new Peer() or you may miss the event if you wait too long to listen for it.
ok thanks for the patience, as soon as I implement the code I will post the result :+1:
No this continues to not work.
I solved with the old solution.
`
sendData(data) {
this._conn.on('open', function(){
this.send(data);
});
}`
this can be considered a good comromise? In this way, i can't create new listener but i use the same connection. One solution more refined is: use this function only one time and then send the data with
`
sendData(data) {
this._conn.send(data);
}`
But i want tested before. Do you think is a good solution ?
I'm afraid we will need to continue tomorrow. Would you like to upload your code to https://gist.github.com/ ?
So I can have a better look, you can make it private and send me the link via email if you don't want others to look at it.
My email: [email protected]
thank you so much for the opportunity. At the moment my code is an open construction site. As soon as I have arranged it I will make it available. In the meantime, when you have time, could you tell me if the last solution I indicated could be a good approach to the problem?
Thanks again and see you soon :)
The second solution would be much recommended. The first one will give you a lot of troubles because you are creating a lot of connections, one per message, and that will not only lead you to the first error you got from PeerJS, but it will also beat your resources (cpu, ram, network)
I confirm that with the second solution I avoid the error. For this, now I have only two connection:
Most helpful comment
I confirm that with the second solution I avoid the error. For this, now I have only two connection:
The other exchange of data is execute with the same connection. Thank @kidandcat for your support. :+1: