Multiple fixes for the P2P feature (#1967)
* Fix "Send variable to all peers" action * Multiple disconnection from remote instances can now be tracked using events * Add a condition to detect when another instance connects remotely to the current instance
@@ -8,6 +8,7 @@
|
||||
gdjs.evtTools.p2p = {
|
||||
/**
|
||||
* The peer to peer configuration.
|
||||
* @type {Peer.PeerJSOption}
|
||||
*/
|
||||
peerConfig: { debug: 1 }, // Enable logging of critical errors
|
||||
|
||||
@@ -18,52 +19,75 @@ gdjs.evtTools.p2p = {
|
||||
peer: null,
|
||||
|
||||
/**
|
||||
* All connected p2p clients, keyed by their id.
|
||||
* All connected p2p clients, keyed by their ID.
|
||||
* @type {Object<string, Peer.DataConnection>}
|
||||
*/
|
||||
connections: {},
|
||||
|
||||
/**
|
||||
* Contains a list of events triggered by other p2p clients.
|
||||
* Maps an event name (string) to a boolean:
|
||||
* true if the event has been triggered, otherwise false.
|
||||
* @note This is ignored if the event is in no dataloss mode.
|
||||
* @type {Object<string, boolean>}
|
||||
*/
|
||||
triggeredEvents: {},
|
||||
|
||||
/**
|
||||
* Contains the latest data sent with each event.
|
||||
* If the event is in dataloss mode, maps an event name (string)
|
||||
* to the string sent with that event.
|
||||
* If the event is in no dataloss mode, maps an event name (string)
|
||||
* to an array containing the data of each call of that event.
|
||||
* @type {Object<string, string | Array>}
|
||||
*/
|
||||
lastEventData: {},
|
||||
|
||||
/**
|
||||
* Tells how to handle an event (with or without data loss)
|
||||
* Tells how to handle an event (with or without data loss).
|
||||
* Maps the event name (string) to a boolean:
|
||||
* true for dataloss, false for no dataloss.
|
||||
* @type {Object<string, string>}
|
||||
*/
|
||||
eventHandling: {},
|
||||
|
||||
/**
|
||||
* True if PeerJS is initialized and ready.
|
||||
* @type {boolean}
|
||||
*/
|
||||
ready: false,
|
||||
|
||||
/**
|
||||
* True if an error occured.
|
||||
* @type {boolean}
|
||||
*/
|
||||
error: false,
|
||||
|
||||
/**
|
||||
* Last error's message.
|
||||
* @type {string}
|
||||
*/
|
||||
lastError: '',
|
||||
|
||||
/**
|
||||
* True if a peer diconnected.
|
||||
* List of IDs of peers that just disconnected.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
peerJustDisconnected: false,
|
||||
disconnectedPeers: [],
|
||||
|
||||
/**
|
||||
* The last peer that has disconnected.
|
||||
* List of IDs of peers that just remotely initiated a connection.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
lastDisconnectedPeerId: '',
|
||||
connectedPeers: [],
|
||||
};
|
||||
|
||||
gdjs.evtTools.p2p.loadPeerJS = function () {
|
||||
/**
|
||||
* Internal function called to initialize PeerJS after its
|
||||
* broker server has been configured.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.p2p._loadPeerJS = function () {
|
||||
if (gdjs.evtTools.p2p.peer != null) return;
|
||||
gdjs.evtTools.p2p.peer = new Peer(gdjs.evtTools.p2p.peerConfig);
|
||||
gdjs.evtTools.p2p.peer.on('open', function () {
|
||||
@@ -73,14 +97,24 @@ gdjs.evtTools.p2p.loadPeerJS = function () {
|
||||
gdjs.evtTools.p2p.error = true;
|
||||
gdjs.evtTools.p2p.lastError = errorMessage;
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('connection', gdjs.evtTools.p2p._onConnection);
|
||||
gdjs.evtTools.p2p.peer.on('connection', function (connection) {
|
||||
connection.on('open', function () {
|
||||
gdjs.evtTools.p2p._onConnection(connection);
|
||||
gdjs.evtTools.p2p.connectedPeers.push(connection.peer);
|
||||
});
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('close', function () {
|
||||
gdjs.evtTools.p2p.peer = null;
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
gdjs.evtTools.p2p._loadPeerJS();
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('disconnected', gdjs.evtTools.p2p.peer.reconnect);
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function called when a connection with a remote peer is initiated.
|
||||
* @private
|
||||
* @param {Peer.DataConnection} connection The DataConnection of the peer
|
||||
*/
|
||||
gdjs.evtTools.p2p._onConnection = function (connection) {
|
||||
gdjs.evtTools.p2p.connections[connection.peer] = connection;
|
||||
connection.on('data', function (data) {
|
||||
@@ -117,15 +151,19 @@ gdjs.evtTools.p2p._onConnection = function (connection) {
|
||||
disconnectChecker();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function called when a remote client disconnects.
|
||||
* @private
|
||||
* @param {string} connectionID The ID of the peer that disconnected.
|
||||
*/
|
||||
gdjs.evtTools.p2p._onDisconnect = function (connectionID) {
|
||||
gdjs.evtTools.p2p.peerJustDisconnected = true;
|
||||
gdjs.evtTools.p2p.lastDisconnectedPeerId = connectionID;
|
||||
gdjs.evtTools.p2p.disconnectedPeers.push(connectionID);
|
||||
delete gdjs.evtTools.p2p.connections[connectionID];
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects to another p2p client.
|
||||
* @param {string} id - The other client's id.
|
||||
* @param {string} id - The other client's ID.
|
||||
*/
|
||||
gdjs.evtTools.p2p.connect = function (id) {
|
||||
var connection = gdjs.evtTools.p2p.peer.connect(id);
|
||||
@@ -137,14 +175,14 @@ gdjs.evtTools.p2p.connect = function (id) {
|
||||
/**
|
||||
* Returns true when the event got triggered by another p2p client.
|
||||
* @param {string} eventName
|
||||
* @param {boolean} _dataLoss Is data loss allowed (accelerates event handling when true)?
|
||||
* @param {boolean} defaultDataLoss Is data loss allowed (accelerates event handling when true)?
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onEvent = function (eventName, _dataLoss) {
|
||||
gdjs.evtTools.p2p.onEvent = function (eventName, defaultDataLoss) {
|
||||
var dataLoss = gdjs.evtTools.p2p.eventHandling[eventName];
|
||||
if (dataLoss == undefined) {
|
||||
gdjs.evtTools.p2p.eventHandling[eventName] = _dataLoss;
|
||||
return gdjs.evtTools.p2p.onEvent(eventName, _dataLoss);
|
||||
gdjs.evtTools.p2p.eventHandling[eventName] = defaultDataLoss;
|
||||
return gdjs.evtTools.p2p.onEvent(eventName, defaultDataLoss);
|
||||
}
|
||||
if (dataLoss) {
|
||||
var returnValue = gdjs.evtTools.p2p.triggeredEvents[eventName];
|
||||
@@ -160,7 +198,7 @@ gdjs.evtTools.p2p.onEvent = function (eventName, _dataLoss) {
|
||||
|
||||
/**
|
||||
* Send an event to one specific connected client.
|
||||
* @param {string} id - The id of the client to send the event to.
|
||||
* @param {string} id - The ID of the client to send the event to.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {string} [eventData] - Additional data to send with the event.
|
||||
*/
|
||||
@@ -188,16 +226,16 @@ gdjs.evtTools.p2p.sendDataToAll = function (eventName, eventData) {
|
||||
|
||||
/**
|
||||
* Send an event to one specific connected client.
|
||||
* @param {string} id - The id of the client to send the event to.
|
||||
* @param {string} id - The ID of the client to send the event to.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {gdjs.Variable} variable - Additional variable to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendVariableTo = function (id, eventName, variable) {
|
||||
if (gdjs.evtTools.p2p.connections[id])
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: gdjs.evtTools.network.variableStructureToJSON(variable),
|
||||
});
|
||||
gdjs.evtTools.p2p.sendDataTo(
|
||||
id,
|
||||
eventName,
|
||||
gdjs.evtTools.network.variableStructureToJSON(variable)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -206,12 +244,10 @@ gdjs.evtTools.p2p.sendVariableTo = function (id, eventName, variable) {
|
||||
* @param {gdjs.Variable} variable - Additional variable to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendVariableToAll = function (eventName, variable) {
|
||||
for (var id in gdjs.evtTools.p2p.connections) {
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: gdjs.evtTools.network.variableStructureToJSON(variable),
|
||||
});
|
||||
}
|
||||
gdjs.evtTools.p2p.sendDataToAll(
|
||||
eventName,
|
||||
gdjs.evtTools.network.variableStructureToJSON(variable)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -265,7 +301,7 @@ gdjs.evtTools.p2p.useCustomBrokerServer = function (
|
||||
secure: ssl,
|
||||
key,
|
||||
};
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
gdjs.evtTools.p2p._loadPeerJS();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -274,11 +310,11 @@ gdjs.evtTools.p2p.useCustomBrokerServer = function (
|
||||
* this server should only be used for quick testing in development.
|
||||
*/
|
||||
gdjs.evtTools.p2p.useDefaultBrokerServer = function () {
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
gdjs.evtTools.p2p._loadPeerJS();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the own current peer ID
|
||||
* Returns the own current peer ID.
|
||||
* @see Peer.id
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -288,7 +324,7 @@ gdjs.evtTools.p2p.getCurrentId = function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true once PeerJS is initialized
|
||||
* Returns true once PeerJS finished initialization.
|
||||
* @see gdjs.evtTools.p2p.ready
|
||||
* @returns {boolean}
|
||||
*/
|
||||
@@ -319,13 +355,39 @@ gdjs.evtTools.p2p.getLastError = function () {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onDisconnect = function () {
|
||||
var returnValue = gdjs.evtTools.p2p.peerJustDisconnected;
|
||||
gdjs.evtTools.p2p.peerJustDisconnected = false;
|
||||
return returnValue;
|
||||
return gdjs.evtTools.p2p.disconnectedPeers.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the ID of the peer that triggered onDisconnect.
|
||||
* @returns {string}
|
||||
*/
|
||||
gdjs.evtTools.p2p.getDisconnectedPeer = function () {
|
||||
return gdjs.evtTools.p2p.lastDisconnectedPeerId;
|
||||
return (
|
||||
gdjs.evtTools.p2p.disconnectedPeers[
|
||||
gdjs.evtTools.p2p.disconnectedPeers.length - 1
|
||||
] || ''
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true once if a remote peer just initiated a connection.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onConnection = function () {
|
||||
return gdjs.evtTools.p2p.connectedPeers.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the ID of the peer that triggered onConnection.
|
||||
* @returns {string}
|
||||
*/
|
||||
gdjs.evtTools.p2p.getConnectedPeer = function () {
|
||||
return (
|
||||
gdjs.evtTools.p2p.connectedPeers[
|
||||
gdjs.evtTools.p2p.connectedPeers.length - 1
|
||||
] || ''
|
||||
);
|
||||
};
|
||||
|
||||
gdjs.callbacksRuntimeScenePostEvents.push(function () {
|
||||
@@ -336,4 +398,8 @@ gdjs.callbacksRuntimeScenePostEvents.push(function () {
|
||||
)
|
||||
gdjs.evtTools.p2p.lastEventData[i].pop();
|
||||
}
|
||||
if (gdjs.evtTools.p2p.disconnectedPeers.length > 0)
|
||||
gdjs.evtTools.p2p.disconnectedPeers.pop();
|
||||
if (gdjs.evtTools.p2p.connectedPeers.length > 0)
|
||||
gdjs.evtTools.p2p.connectedPeers.pop();
|
||||
});
|
||||
|
@@ -105,6 +105,21 @@ module.exports = {
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.onDisconnect');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'OnConnection',
|
||||
_('Peer Connected'),
|
||||
_('Triggers once when a remote peer initiates a connection.'),
|
||||
_('P2P peer connected'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.onConnection');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Connect',
|
||||
@@ -215,7 +230,7 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendDataToAll');
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendVariableToAll');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
@@ -313,7 +328,7 @@ module.exports = {
|
||||
.addStrExpression(
|
||||
'GetLastDisconnectedPeer',
|
||||
_('Get last disconnected peer'),
|
||||
_('Gets the id of the latest peer that has disconnected.'),
|
||||
_('Gets the ID of the latest peer that has disconnected.'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
@@ -322,6 +337,19 @@ module.exports = {
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getDisconnectedPeer');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetLastConnectedPeer',
|
||||
_('Get ID of the connected peer'),
|
||||
_('Gets the ID of the newly connected peer.'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getConnectedPeer');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function (
|
||||
|
@@ -1 +0,0 @@
|
||||
A showcase of some multiplayer possibilities using the P2P extension.
|
Before Width: | Height: | Size: 629 B After Width: | Height: | Size: 629 B |
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
Before Width: | Height: | Size: 382 B After Width: | Height: | Size: 382 B |
Before Width: | Height: | Size: 650 B After Width: | Height: | Size: 650 B |
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 189 B |
1
newIDE/app/resources/examples/p2p-networking/README.md
Normal file
@@ -0,0 +1 @@
|
||||
A showcase of some multiplayer/networking possibilities using the P2P extension.
|
@@ -7,7 +7,6 @@
|
||||
"revision": 0
|
||||
},
|
||||
"properties": {
|
||||
"adMobAppId": "",
|
||||
"adaptGameResolutionAtRuntime": true,
|
||||
"folderProject": false,
|
||||
"linuxExecutableFilename": "",
|
||||
@@ -21,7 +20,7 @@
|
||||
"version": "1.0.0",
|
||||
"winExecutableFilename": "",
|
||||
"winExecutableIconFile": "",
|
||||
"name": "Multiplayer Game Example",
|
||||
"name": "P2P Networking Example",
|
||||
"author": "Arthur Pacaud (arthuro555)",
|
||||
"windowWidth": 800,
|
||||
"windowHeight": 600,
|
||||
@@ -33,6 +32,7 @@
|
||||
"loadingScreen": {
|
||||
"showGDevelopSplash": false
|
||||
},
|
||||
"extensionProperties": [],
|
||||
"extensions": [
|
||||
{
|
||||
"name": "BuiltinObject"
|
||||
@@ -1079,7 +1079,7 @@
|
||||
"textG": 0,
|
||||
"textR": 0
|
||||
},
|
||||
"comment": "Try to verify if connection has happened every second",
|
||||
"comment": "When a peer is connected, confirm connection and switch to the game",
|
||||
"comment2": ""
|
||||
},
|
||||
{
|
||||
@@ -1090,28 +1090,13 @@
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Timer"
|
||||
"value": "P2P::OnConnection"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"1",
|
||||
"\"verifyConnection\""
|
||||
],
|
||||
"parameters": [],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "ResetTimer"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"\"verifyConnection\""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
@@ -1122,6 +1107,18 @@
|
||||
"\"\""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Scene"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"\"Menu\"",
|
||||
"yes"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
@@ -1138,7 +1135,7 @@
|
||||
"textG": 0,
|
||||
"textR": 0
|
||||
},
|
||||
"comment": "Switch to the game once connected and notify other instance that the connection has happened",
|
||||
"comment": "Switch to the game once connection is confirmed",
|
||||
"comment2": ""
|
||||
},
|
||||
{
|
||||
@@ -1159,17 +1156,6 @@
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "P2P::SendToAll"
|
||||
},
|
||||
"parameters": [
|
||||
"\"connected\"",
|
||||
"\"\""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
@@ -1357,6 +1343,11 @@
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"ambientLightColorB": 200,
|
||||
"ambientLightColorG": 200,
|
||||
"ambientLightColorR": 200,
|
||||
"followBaseLayerCamera": false,
|
||||
"isLightingLayer": false,
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
@@ -2036,6 +2027,11 @@
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"ambientLightColorB": 200,
|
||||
"ambientLightColorG": 200,
|
||||
"ambientLightColorR": 200,
|
||||
"followBaseLayerCamera": false,
|
||||
"isLightingLayer": false,
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
@@ -2626,6 +2622,11 @@
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"ambientLightColorB": 200,
|
||||
"ambientLightColorG": 200,
|
||||
"ambientLightColorR": 200,
|
||||
"followBaseLayerCamera": false,
|
||||
"isLightingLayer": false,
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
@@ -3334,6 +3335,11 @@
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"ambientLightColorB": 200,
|
||||
"ambientLightColorG": 200,
|
||||
"ambientLightColorR": 200,
|
||||
"followBaseLayerCamera": false,
|
||||
"isLightingLayer": false,
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
@@ -3491,6 +3497,11 @@
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"ambientLightColorB": 200,
|
||||
"ambientLightColorG": 200,
|
||||
"ambientLightColorR": 200,
|
||||
"followBaseLayerCamera": false,
|
||||
"isLightingLayer": false,
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|