mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-23 00:06:15 -05:00
Fix an extremely rare race condition in battle preparation
By the way promises work, the `team` property assigned in User#finishPrepBattle() is not guaranteed to remain the same by the time the resolution callback is called. To fix this, the packed team is now directly passed to the callback instead.
This commit is contained in:
parent
31fe2aad7e
commit
36ab0a2e57
|
|
@ -3265,8 +3265,10 @@ exports.commands = {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
user.prepBattle(Dex.getFormat(target).id, 'challenge', connection).then(result => {
|
||||
if (result) user.makeChallenge(targetUser, target);
|
||||
user.prepBattle(Dex.getFormat(target).id, 'challenge', connection).then(validTeam => {
|
||||
if (validTeam === false) return;
|
||||
user.team = validTeam;
|
||||
user.makeChallenge(targetUser, target);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -3309,8 +3311,10 @@ exports.commands = {
|
|||
this.popupReply(target + " isn't challenging you - maybe they cancelled before you could accept?");
|
||||
return false;
|
||||
}
|
||||
user.prepBattle(Dex.getFormat(format).id, 'challenge', connection).then(result => {
|
||||
if (result) user.acceptChallengeFrom(userid);
|
||||
user.prepBattle(Dex.getFormat(format).id, 'challenge', connection).then(validTeam => {
|
||||
if (validTeam === false) return;
|
||||
user.team = validTeam;
|
||||
user.acceptChallengeFrom(userid);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -62,13 +62,13 @@ class Matchmaker {
|
|||
if (!user.connected) return;
|
||||
formatid = Dex.getFormat(formatid).id;
|
||||
return user.prepBattle(formatid, 'search', null)
|
||||
.then(result => this.finishSearchBattle(user, formatid, result));
|
||||
.then(validTeam => this.finishSearchBattle(user, formatid, validTeam));
|
||||
}
|
||||
|
||||
finishSearchBattle(user, formatid, result) {
|
||||
if (!result) return;
|
||||
finishSearchBattle(user, formatid, validTeam) {
|
||||
if (validTeam === false) return;
|
||||
|
||||
const search = new Search(user.userid, user.team);
|
||||
const search = new Search(user.userid, validTeam);
|
||||
// Get the user's rating before actually starting to search.
|
||||
Ladders(formatid).getRating(user.userid).then(rating => {
|
||||
search.setRating(rating);
|
||||
|
|
|
|||
|
|
@ -712,11 +712,11 @@ class Tournament {
|
|||
this.isAvailableMatchesInvalidated = true;
|
||||
this.update();
|
||||
|
||||
user.prepBattle(this.teambuilderFormat, 'tournament', user, this.banlist).then(result => this.finishChallenge(user, to, output, result));
|
||||
user.prepBattle(this.teambuilderFormat, 'tournament', user, this.banlist).then(validTeam => this.finishChallenge(user, to, output, validTeam));
|
||||
}
|
||||
finishChallenge(user, to, output, result) {
|
||||
finishChallenge(user, to, output, validTeam) {
|
||||
let from = this.players[user.userid];
|
||||
if (!result) {
|
||||
if (validTeam === false) {
|
||||
this.generator.setUserBusy(from, false);
|
||||
this.generator.setUserBusy(to, false);
|
||||
|
||||
|
|
@ -726,8 +726,8 @@ class Tournament {
|
|||
}
|
||||
|
||||
this.lastActionTimes.set(to, Date.now());
|
||||
this.pendingChallenges.set(from, {to: to, team: user.team});
|
||||
this.pendingChallenges.set(to, {from: from, team: user.team});
|
||||
this.pendingChallenges.set(from, {to: to, team: validTeam});
|
||||
this.pendingChallenges.set(to, {from: from, team: validTeam});
|
||||
from.sendRoom('|tournament|update|' + JSON.stringify({challenging: to.name}));
|
||||
to.sendRoom('|tournament|update|' + JSON.stringify({challenged: from.name}));
|
||||
|
||||
|
|
|
|||
16
users.js
16
users.js
|
|
@ -1283,16 +1283,26 @@ class User {
|
|||
}
|
||||
return TeamValidator(formatid, customBanlist).prepTeam(this.team, this.locked || this.namelocked).then(result => this.finishPrepBattle(connection, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the result of a team validation and notifies the user.
|
||||
*
|
||||
* @param {Connection} connection - The connection from which the team validation was requested.
|
||||
* @param {string} result - The raw result received by the team validator.
|
||||
* @return {string|boolean} - The packed team if the validation was successful. False otherwise.
|
||||
*/
|
||||
finishPrepBattle(connection, result) {
|
||||
if (result.charAt(0) !== '1') {
|
||||
connection.popup(`Your team was rejected for the following reasons:\n\n- ` + result.slice(1).replace(/\n/g, `\n- `));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.length > 1) {
|
||||
this.team = result.slice(1);
|
||||
if (this !== users.get(this.userid)) {
|
||||
// TODO: User feedback.
|
||||
return false;
|
||||
}
|
||||
return (this === users.get(this.userid));
|
||||
|
||||
return result.slice(1);
|
||||
}
|
||||
updateChallenges() {
|
||||
let challengeTo = this.challengeTo;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user