Commit ae02c048 authored by Hugo "ThePooN" Denizart's avatar Hugo "ThePooN" Denizart

Merge branch '25-add-multiplayer-scores-sorting' into 'master'

Resolve "Add multiplayer scores sorting"

Closes #25

See merge request !2
parents 411bcffc fd5c3b6e
Pipeline #2045 passed with stage
in 1 minute and 15 seconds
const Banchojs = require(".."); // Replace .. by bancho.js when coding outside of the library
const client = new Banchojs.BanchoClient(require("../config.json"));
const threshold = new Date("2014-01-01");
const channelName = "#mp_43508978";
client.connect().then(async () => {
console.log("We're online!");
const channel = client.getChannel(channelName);
await channel.join();
const lobby = channel.lobby;
if(lobby == null)
throw new Error("missing api key");
console.log("Multiplayer link: https://osu.ppy.sh/mp/"+lobby.id);
lobby.on("beatmap", async (beatmap) => {
if(beatmap != null && Math.max(beatmap.lastUpdate.getTime(), beatmap.approvedDate.getTime()) > threshold.getTime())
await channel.sendMessage("This beatmap is too recent! Only beatmaps ranked in 2013 and before are accepted.");
});
}).catch(console.error);
process.on("SIGINT", async () => {
console.log("Disconnecting...");
await client.disconnect();
});
\ No newline at end of file
const Banchojs = require(".."); // Replace .. by bancho.js when coding outside of the library
const client = new Banchojs.BanchoClient(require("../config.json"));
client.connect().then(async () => {
console.log("We're online!");
const channel = client.getChannel("#mp_43508978");
await channel.join();
const lobby = channel.lobby;
if(lobby == null)
throw new Error("missing api key");
console.log("Multiplayer link: https://osu.ppy.sh/mp/"+lobby.id);
lobby.on("matchStart", () => {
console.log("Match on "+lobby.beatmap.id+" started...");
});
lobby.on("matchFinished", (scores) => {
console.log("Results are in!");
for(let scoreId in scores)
console.log("#"+(Number(scoreId)+1)+": "+scores[scoreId].player.user.username+" "+scores[scoreId].score+" "+scores[scoreId].pass);
});
}).catch(console.error);
process.on("SIGINT", async () => {
console.log("Disconnecting...");
await client.disconnect();
});
\ No newline at end of file
......@@ -310,6 +310,10 @@ declare module "bancho.js" {
* Multiplayer lobby ID (used in multiplayer history links)
*/
id: number
/**
* Multiplayer lobby ID (used in multiplayer history links)
*/
scores: Array<BanchoLobbyPlayerScore>
/**
* Name of the lobby, as seein in-game
*/
......@@ -490,6 +494,11 @@ declare module "bancho.js" {
*/
getHistoryUrl(): string
/**
* Sort scores by pass and score.
*/
sortScores(): void
on(event: "allPlayersReady", listener: () => void): this
/**
......
......@@ -23,6 +23,7 @@ const Teams = require("./Enums/BanchoLobbyTeams");
* @prop {Array<BanchoMod>} mods
* @prop {boolean} freemod
* @prop {boolean} playing Whether we're currently playing or not
* @prop {Array<BanchoLobbyPlayerScore>} scores Scores set during the currently ongoing match, or the previous match. Emptied when a new match starts. Sorted by pass and score once match is finished.
*/
class BanchoLobby extends EventEmitter {
constructor(channel) {
......@@ -30,6 +31,7 @@ class BanchoLobby extends EventEmitter {
this.channel = channel;
this.banchojs = this.channel.banchojs;
this.id = Number(channel.name.substring("#mp_".length));
this.scores = [];
this._name = "";
this._beatmapId = null;
this._beatmap = null;
......@@ -289,6 +291,7 @@ class BanchoLobby extends EventEmitter {
player.score = null;
}
}
this.scores.length = 0;
this.playing = true;
this.emit("matchStarted");
break;
......@@ -299,19 +302,33 @@ class BanchoLobby extends EventEmitter {
*/
this.getPlayerByName(ret.username).then((player) => {
player.score = new BanchoLobbyPlayerScore(ret.score, ret.pass, player);
this.scores.push(player.score);
this.emit("playerFinished", player.score);
}, (err) => this.banchojs.emit("error", err));
break;
case "matchFinished":
case "matchAborted":
/**
* @event BanchoLobby#matchFinished
*/
/**
* @event BanchoLobby#matchAborted
*/
this.playing = false;
this.emit(regex.name);
this.emit("matchAborted");
break;
case "matchFinished":
/**
* @event BanchoLobby#matchFinished
* @type {Array.<BanchoLobbyPlayerScore>} Sorted scores array
*/
// Due to players resolving, matchFinished may be emitted and scores sorted before every playerFinished are done being processed.
// For some reason, even though a Promise may not have anything async, the "then" functions may still be executed asynchronously...
// However, they seem to be executed in order for some reason. So a workaround for this is to wait for the callback of a resolved Promise to be called.
Promise.resolve().then(() => {
// This ensures sorting/emitting is executed after all names are resolved.
this.pushPlayerCreationQueue(() => {
this.sortScores();
this.emit(regex.name, this.scores);
this.playersCreationCallback();
});
});
break;
case "invalidBeatmapId":
case "passwordRemoved":
......@@ -944,6 +961,24 @@ class BanchoLobby extends EventEmitter {
return Object.seal(slots);
}
/**
* Sort scores by pass and score.
*/
sortScores() {
this.scores.sort((a, b) => {
if(a.pass && !b.pass)
return -1;
else if(!a.pass && b.pass)
return 1;
else if(a.score > b.score)
return -1;
else if(b.score > a.score)
return 1;
else
return 0;
});
}
get beatmap() {
return this._beatmap;
}
......
......@@ -185,7 +185,7 @@ module.exports = {
const m = r.exec(str);
return {
username: m[1],
score: m[2],
score: Number(m[2]),
pass: m[3] == "PASS"
};
}
......
......@@ -16,6 +16,7 @@ const TestGoals = {
Where: Symbol("Where"),
Stats: Symbol("Stats"),
BanchoMods: Symbol("BanchoMods"),
MultiplayerScoresSorting: Symbol("MultiplayerScoresSorting"),
MultiplayerLobbyExists: Symbol("MultiplayerLobbyExists"),
MultiplayerLobbySetMap: Symbol("MultiplayerLobbySetMap"),
......
const TestUnit = require("../TestUnit").TestUnit;
const TestGoals = require("../TestGoals");
const BanchoLobbyPlayerScore = require("../../lib/Multiplayer/BanchoLobbyPlayerScore");
const BanchoLobby = require("../../lib/Multiplayer/BanchoLobby");
class MultiplayerScoresSortingUnit extends TestUnit {
constructor() {
super();
this.name = "MultiplayerScoresSortingUnit";
}
async run() {
const _this = {
scores: [
new BanchoLobbyPlayerScore(500000, false, null),
new BanchoLobbyPlayerScore(500000, true, null),
new BanchoLobbyPlayerScore(1000000, true, null),
new BanchoLobbyPlayerScore(800000, true, null),
new BanchoLobbyPlayerScore(900000, false, null)
]
};
const expectedScores = [
new BanchoLobbyPlayerScore(1000000, true, null),
new BanchoLobbyPlayerScore(800000, true, null),
new BanchoLobbyPlayerScore(500000, true, null),
new BanchoLobbyPlayerScore(900000, false, null),
new BanchoLobbyPlayerScore(500000, false, null)
];
BanchoLobby.prototype.sortScores.apply(_this);
if(JSON.stringify(_this.scores) == JSON.stringify(expectedScores))
this.fulFillGoal(TestGoals.MultiplayerScoresSorting);
else
throw new Error("Sorted scores and expected sorting don't match! expected: " + JSON.stringify(expectedScores) + ", got: " + JSON.stringify(_this.scores));
}
}
module.exports = new MultiplayerScoresSortingUnit();
\ No newline at end of file
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