BanchoLobby: rewrite synchronous queues

Closes #31
parent 9c209bd1
......@@ -47,6 +47,7 @@ class BanchoLobby extends EventEmitter {
this.playersById = {};
this.slotsUpdatesQueue = [];
this.playerCreationQueue = [];
this.channel.on("JOIN", (member) => {
if(member.user.isClient()) // We have just joined the channel
......@@ -67,22 +68,42 @@ class BanchoLobby extends EventEmitter {
* @param {function} func
*/
pushSlotsUpdateQueue(func) {
this.slotsUpdatesQueue.push(() => {
func // Execute current function
.then(() => {
this.slotsUpdatesQueue.shift(); // Remove current function from queue
if(this.slotsUpdatesQueue[0])
this.slotsUpdatesQueue[0](); // Execute next function, if any
})
.catch((err) => {
this.banchojs.emit("error", err);
this.slotsUpdatesQueue.shift(); // why isn't .finally() implemented. Promises please.
if(this.slotsUpdatesQueue[0])
this.slotsUpdatesQueue[0]();
});
});
this.slotsUpdatesQueue.push(func);
if(this.slotsUpdatesQueue.length == 1)
this.slotsUpdatesQueue[0](); // If there's no currently running function, execute the newest now
this.slotsUpdatesQueue[0](this.slotsUpdateCallback); // If there's no currently running function, execute the newest now
}
/**
* Called by a function in the slots update queue when the next one can be called.
* @private
*/
slotsUpdateCallback() {
this.slotsUpdatesQueue.shift();
if(this.slotsUpdatesQueue[0])
this.slotsUpdatesQueue[0](this.slotsUpdateCallback);
}
/**
* Synchronous handling of BanchoLobbyPlayer object creations.
* As every first fetch of a BanchoLobbyPlayer object involves an API call and
* we only want one BanchoLobbyPlayer at a time, we need to make creations of them async.
* @private
* @param {function} func
*/
pushPlayerCreationQueue(func) {
this.playerCreationQueue.push(func);
if(this.playerCreationQueue.length == 1)
this.playerCreationQueue[0](this.playersCreationCallback); // If there's no currently running function, execute the newest now
}
/**
* Called by a function in the players creation queue when the next one can be called.
* @private
*/
playersCreationCallback() {
this.playerCreationQueue.shift();
if(this.playerCreationQueue[0])
this.playerCreationQueue[0](this.playersCreationCallback);
}
/**
......@@ -165,7 +186,7 @@ class BanchoLobby extends EventEmitter {
player.state = BanchoLobbyPlayerStates.NotReady;
break;
case "playerJoined":
this.pushSlotsUpdateQueue(new Promise((resolve, reject) => {
this.pushSlotsUpdateQueue(() => {
this.getPlayerByName(ret.username).then((player) => {
/**
* @event BanchoLobby#playerJoined
......@@ -183,12 +204,12 @@ class BanchoLobby extends EventEmitter {
team: ret.team
});
this.emit("slots", this.slots);
resolve();
}, reject);
}));
this.slotsUpdateCallback();
}, (err) => { this.banchojs.emit("error", err); this.slotsUpdateCallback(); });
});
break;
case "playerMoved":
this.pushSlotsUpdateQueue(new Promise((resolve, reject) => {
this.pushSlotsUpdateQueue(() => {
this.getPlayerByName(ret.username).then((player) => {
/**
* @event BanchoLobby#playerMoved
......@@ -204,26 +225,28 @@ class BanchoLobby extends EventEmitter {
slot: (ret.slot - 1)
});
this.emit("slots", this.slots);
resolve();
}, reject);
}));
this.slotsUpdateCallback();
}, (err) => { this.banchojs.emit("error", err); this.slotsUpdateCallback(); });
});
break;
case "playerLeft":
this.pushSlotsUpdateQueue(new Promise((resolve, reject) => {
this.pushSlotsUpdateQueue(() => {
this.getPlayerByName(ret.username).then((player) => {
/**
* @event BanchoLobby#playerLeft
* @type {BanchoLobbyPlayer}
*/
this.slots[this.getPlayerSlot(player)] = null;
const slot = this.getPlayerSlot(player);
this.slots[slot] = null;
this.emit("playerLeft", player);
if(player.isHost) {
this.emit("hostCleared");
this.emit("host", null);
}
this.emit("slots", this.slots);
}, reject);
}));
this.slotsUpdateCallback();
}, (err) => { this.banchojs.emit("error", err); this.slotsUpdateCallback(); });
});
break;
case "playerBecameTheHost":
this.getPlayerByName(ret.username).then((player) => {
......@@ -735,7 +758,7 @@ class BanchoLobby extends EventEmitter {
return this.updateSettingsPromise;
return this.updateSettingsPromise = new Promise((callerResolve, callerReject) => {
this.pushSlotsUpdateQueue(new Promise((resolve, reject) => {
this.pushSlotsUpdateQueue(() => {
this.channel.sendMessage("!mp settings "+this.randomString());
// Beginning of the !mp settings message will be handled by handleBanchoBotMessage; ending (player infos) will be handled in the following listener.
let amountOfPlayers = null;
......@@ -748,7 +771,7 @@ class BanchoLobby extends EventEmitter {
amountOfPlayers = playersRegex.playersAmount;
if(amountOfPlayers == 0) {
this.slots = this._createSlotsArray();
resolve();
this.slotsUpdateCallback();
callerResolve();
this.updateSettingsPromise = null;
this.channel.removeListener("message", listener);
......@@ -793,13 +816,13 @@ class BanchoLobby extends EventEmitter {
}
if(completedPlayers == amountOfPlayers) {
this.slots = slots;
resolve();
this.slotsUpdateCallback();
callerResolve();
this.updateSettingsPromise = null;
this.channel.removeListener("message", listener);
}
}, (err) => {
reject(err);
this.slotsUpdateCallback();
callerReject(err);
});
}
......@@ -808,7 +831,7 @@ class BanchoLobby extends EventEmitter {
};
this.channel.on("message", listener);
}));
});
});
}
......@@ -839,22 +862,33 @@ class BanchoLobby extends EventEmitter {
* @async
*/
getPlayerByName(name) {
return new Promise((resolve, reject) => {
return new Promise((callerResolve, callerReject) => {
if(this.players[name])
resolve(this.players[name]);
else {
const user = this.banchojs.getUser(name);
const player = new BanchoLobbyPlayer(this, user);
const cb = () => {
this.playersById[user.id] = player;
this.players[user.username] = player;
resolve(player);
};
if(!user.username || !user.id)
user.fetchFromAPI().then(cb).catch(reject);
else
cb();
}
return callerResolve(this.players[name]);
this.pushPlayerCreationQueue(() => {
if(this.players[name]) {
callerResolve(this.players[name]);
this.playersCreationCallback();
}
else {
const user = this.banchojs.getUser(name);
const player = new BanchoLobbyPlayer(this, user);
const cb = () => {
this.playersById[user.id] = player;
this.players[user.username] = player;
callerResolve(player);
this.playersCreationCallback();
};
if(!user.username || !user.id)
user.fetchFromAPI().then(cb).catch((err) => {
callerReject(err);
this.playersCreationCallback();
});
else
cb();
}
});
});
}
......@@ -863,19 +897,27 @@ class BanchoLobby extends EventEmitter {
* @async
*/
getPlayerById(id) {
return new Promise((resolve, reject) => {
return new Promise((callerResolve, callerReject) => {
if(isNaN(id))
reject(new Error("id needs to be a number!"));
return callerReject(new Error("id needs to be a number!"));
if(this.playersById[id])
resolve(this.playersById[id]);
else
this.banchojs.getUserById(id)
.then((user) => {
const player = new BanchoLobbyPlayer(this, this.banchojs.getUser(user.username));
this.playersById[id] = player;
this.players[user.username] = player;
resolve(player);
});
return callerResolve(this.playersById[id]);
this.pushPlayerCreationQueue(() => {
if(this.playersById[id]) {
callerResolve(this.playersById[id]);
this.playersCreationCallback();
}
else
this.banchojs.getUserById(id)
.then((user) => {
const player = new BanchoLobbyPlayer(this, this.banchojs.getUser(user.username));
this.playersById[id] = player;
this.players[user.username] = player;
callerResolve(player);
this.playersCreationCallback();
});
});
});
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment