Multiplayer fixes

This commit is contained in:
Holly Stubbs 2023-09-10 18:32:35 +01:00
parent aff53f1ab9
commit 469cbb9bc9
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E

View file

@ -46,7 +46,7 @@ export default class Match {
public countdownTime:number = 0; public countdownTime:number = 0;
public countdownTimer?:NodeJS.Timeout; public countdownTimer?:NodeJS.Timeout;
private cachedMatchJSON:MatchData; private serialisedMatchJSON:MatchData;
private readonly shared:Shared; private readonly shared:Shared;
private constructor(matchData:MatchData, shared:Shared) { private constructor(matchData:MatchData, shared:Shared) {
@ -95,7 +95,7 @@ export default class Match {
this.matchStream = shared.streams.CreateStream(`multiplayer:match_${this.matchId}`, false); this.matchStream = shared.streams.CreateStream(`multiplayer:match_${this.matchId}`, false);
this.matchChatChannel = shared.chatManager.AddSpecialChatChannel("multiplayer", `mp_${this.matchId}`); this.matchChatChannel = shared.chatManager.AddSpecialChatChannel("multiplayer", `mp_${this.matchId}`);
this.cachedMatchJSON = matchData; this.serialisedMatchJSON = matchData;
//this.multiplayerExtras = null; //this.multiplayerExtras = null;
@ -118,7 +118,7 @@ export default class Match {
const osuPacketWriter = osu.Bancho.Writer(); const osuPacketWriter = osu.Bancho.Writer();
osuPacketWriter.MatchNew(matchInstance.generateMatchJSON()); osuPacketWriter.MatchNew(matchInstance.serialiseMatch());
matchHost.addActionToQueue(osuPacketWriter.toBuffer); matchHost.addActionToQueue(osuPacketWriter.toBuffer);
@ -132,10 +132,27 @@ export default class Match {
} }
// Convert class data back to a format that osu-packet can understand // Convert class data back to a format that osu-packet can understand
public generateMatchJSON() : MatchData { public serialiseMatch() : MatchData {
const matchDataRef = this.serialisedMatchJSON;
matchDataRef.matchId = this.matchId;
matchDataRef.matchType = this.matchType;
matchDataRef.activeMods = this.activeMods;
matchDataRef.gameName = this.gameName;
matchDataRef.gamePassword = this.gamePassword ?? "";
matchDataRef.inProgress = this.inProgress;
matchDataRef.beatmapName = this.beatmapName;
matchDataRef.beatmapId = this.beatmapId;
matchDataRef.beatmapChecksum = this.beatmapChecksum;
matchDataRef.host = this.host.id;
matchDataRef.playMode = this.playMode;
matchDataRef.matchScoringType = this.matchScoringType;
matchDataRef.matchTeamType = this.matchTeamType;
matchDataRef.specialModes = this.specialModes;
matchDataRef.seed = this.seed;
for (let i = 0; i < this.slots.length; i++) { for (let i = 0; i < this.slots.length; i++) {
const slot = this.slots[i]; const slot = this.slots[i];
const osuSlot = this.cachedMatchJSON.slots[i]; const osuSlot = this.serialisedMatchJSON.slots[i];
osuSlot.status = slot.status; osuSlot.status = slot.status;
osuSlot.team = slot.team; osuSlot.team = slot.team;
@ -147,7 +164,7 @@ export default class Match {
} }
} }
return this.cachedMatchJSON; return this.serialisedMatchJSON;
} }
public leaveMatch(user:User) { public leaveMatch(user:User) {
@ -184,12 +201,12 @@ export default class Match {
if (matchData.gamePassword === "") { if (matchData.gamePassword === "") {
this.gamePassword = undefined; this.gamePassword = undefined;
} else { } else {
this.gamePassword = this.cachedMatchJSON.gamePassword = matchData.gamePassword; this.gamePassword = matchData.gamePassword;
} }
this.beatmapName = this.cachedMatchJSON.beatmapName = matchData.beatmapName; this.beatmapName = matchData.beatmapName;
this.beatmapId = this.cachedMatchJSON.beatmapId = matchData.beatmapId; this.beatmapId = matchData.beatmapId;
this.beatmapChecksum = this.cachedMatchJSON.beatmapChecksum = matchData.beatmapChecksum; this.beatmapChecksum = matchData.beatmapChecksum;
if (matchData.host !== this.host.id) { if (matchData.host !== this.host.id) {
const hostUser = this.shared.users.getById(matchData.host); const hostUser = this.shared.users.getById(matchData.host);
@ -198,17 +215,16 @@ export default class Match {
throw "Host User of match was undefined"; throw "Host User of match was undefined";
} }
this.host = hostUser; this.host = hostUser;
this.cachedMatchJSON.host = this.host.id;
} }
this.playMode = this.cachedMatchJSON.playMode = matchData.playMode; this.playMode = matchData.playMode;
this.matchScoringType = this.cachedMatchJSON.matchScoringType = matchData.matchScoringType; this.matchScoringType = matchData.matchScoringType;
this.matchTeamType = this.cachedMatchJSON.matchTeamType = matchData.matchTeamType; this.matchTeamType = matchData.matchTeamType;
this.specialModes = this.cachedMatchJSON.specialModes = matchData.specialModes; this.specialModes = matchData.specialModes;
const gameSeedChanged = this.seed !== matchData.seed; const gameSeedChanged = this.seed !== matchData.seed;
this.seed = this.cachedMatchJSON.seed = matchData.seed; this.seed = matchData.seed;
if (gameNameChanged || gameSeedChanged) { if (gameNameChanged || gameSeedChanged) {
const queryData = []; const queryData = [];
@ -220,7 +236,10 @@ export default class Match {
} }
queryData.push(this.matchId); queryData.push(this.matchId);
await this.shared.database.query(`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`, queryData); await this.shared.database.query(
`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`,
queryData
);
} }
this.sendMatchUpdate(); this.sendMatchUpdate();
@ -232,7 +251,7 @@ export default class Match {
public sendMatchUpdate() { public sendMatchUpdate() {
const osuPacketWriter = osu.Bancho.Writer(); const osuPacketWriter = osu.Bancho.Writer();
osuPacketWriter.MatchUpdate(this.generateMatchJSON()); osuPacketWriter.MatchUpdate(this.serialiseMatch());
// Update all users in the match with new match information // Update all users in the match with new match information
this.matchStream.Send(osuPacketWriter.toBuffer); this.matchStream.Send(osuPacketWriter.toBuffer);
@ -291,17 +310,12 @@ export default class Match {
} }
// Make sure the user attempting to kick / lock is the host of the match // Make sure the user attempting to kick / lock is the host of the match
if (User.Equals(user, this.host)) { if (!User.Equals(user, this.host)) {
return; return;
} }
const slot = this.slots[slotToActionOn]; const slot = this.slots[slotToActionOn];
if (slot.player instanceof Slot) { if (slot.player instanceof User) { // Kick
// Kick
if (User.Equals(user, slot.player)) {
return;
}
const kickedPlayer = slot.player; const kickedPlayer = slot.player;
// Remove player's refs to the match & slot // Remove player's refs to the match & slot
@ -311,15 +325,11 @@ export default class Match {
// Nuke all slot properties // Nuke all slot properties
slot.reset(); slot.reset();
// Send update before removing the user from the stream so they know // Kick player
// they got kicked this.shared.multiplayerManager.LeaveMatch(kickedPlayer);
this.sendMatchUpdate();
// Remove player from stream and chat this.sendMatchUpdate();
this.matchStream.RemoveUser(kickedPlayer); } else { // Lock / Unlock
this.matchChatChannel.Leave(kickedPlayer);
} else {
// Lock / Unlock
slot.status = slot.status === SlotStatus.Empty ? SlotStatus.Locked : SlotStatus.Empty; slot.status = slot.status === SlotStatus.Empty ? SlotStatus.Locked : SlotStatus.Empty;
this.sendMatchUpdate(); this.sendMatchUpdate();
@ -354,7 +364,7 @@ export default class Match {
if (this.matchSkippedSlots === undefined) { if (this.matchSkippedSlots === undefined) {
this.matchSkippedSlots = new Array<MatchStartSkipData>(); this.matchSkippedSlots = new Array<MatchStartSkipData>();
for (let slot of this.slots) { for (const slot of this.slots) {
// Make sure the slot has a user in it // Make sure the slot has a user in it
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {
continue; continue;
@ -369,7 +379,7 @@ export default class Match {
} }
let allSkipped = true; let allSkipped = true;
for (let skippedSlot of this.matchSkippedSlots) { for (const skippedSlot of this.matchSkippedSlots) {
// If loadslot belongs to this user then set loaded to true // If loadslot belongs to this user then set loaded to true
if (skippedSlot.playerId === user.id) { if (skippedSlot.playerId === user.id) {
skippedSlot.flag = true; skippedSlot.flag = true;
@ -403,9 +413,8 @@ export default class Match {
public transferHost(user:User, slotIDToTransferTo:number) { public transferHost(user:User, slotIDToTransferTo:number) {
// Set the lobby's host to the new user // Set the lobby's host to the new user
const newHost = this.slots[slotIDToTransferTo].player; const newHost = this.slots[slotIDToTransferTo].player;
if (newHost instanceof Slot) { if (newHost instanceof User) {
this.host = newHost; this.host = newHost;
this.cachedMatchJSON.host = this.host.id;
this.sendMatchUpdate(); this.sendMatchUpdate();
} }
@ -446,7 +455,7 @@ export default class Match {
this.matchLoadSlots = new Array<MatchStartSkipData>(); this.matchLoadSlots = new Array<MatchStartSkipData>();
// Loop through all slots in the match // Loop through all slots in the match
for (let slot of this.slots) { for (const slot of this.slots) {
// Make sure the slot has a user in it // Make sure the slot has a user in it
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {
continue; continue;
@ -464,7 +473,7 @@ export default class Match {
const osuPacketWriter = osu.Bancho.Writer(); const osuPacketWriter = osu.Bancho.Writer();
osuPacketWriter.MatchStart(this.generateMatchJSON()); osuPacketWriter.MatchStart(this.serialiseMatch());
// Inform all users in the match that it has started // Inform all users in the match that it has started
this.matchStream.Send(osuPacketWriter.toBuffer); this.matchStream.Send(osuPacketWriter.toBuffer);
@ -482,7 +491,7 @@ export default class Match {
} }
let allLoaded = true; let allLoaded = true;
for (let loadedSlot of this.matchLoadSlots) { for (const loadedSlot of this.matchLoadSlots) {
if (loadedSlot.playerId === user.id) { if (loadedSlot.playerId === user.id) {
loadedSlot.flag = true; loadedSlot.flag = true;
} }
@ -502,7 +511,7 @@ export default class Match {
this.matchLoadSlots = undefined; this.matchLoadSlots = undefined;
this.playerScores = new Array<PlayerScore>(); this.playerScores = new Array<PlayerScore>();
for (let slot of this.slots) { for (const slot of this.slots) {
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {
continue; continue;
} }
@ -523,7 +532,7 @@ export default class Match {
if (this.matchLoadSlots === undefined) { if (this.matchLoadSlots === undefined) {
// Repopulate user loading slots again // Repopulate user loading slots again
this.matchLoadSlots = []; this.matchLoadSlots = [];
for (let slot of this.slots) { for (const slot of this.slots) {
// Make sure the slot has a user // Make sure the slot has a user
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {
continue; continue;
@ -538,7 +547,7 @@ export default class Match {
} }
let allLoaded = true; let allLoaded = true;
for (let loadedSlot of this.matchLoadSlots) { for (const loadedSlot of this.matchLoadSlots) {
if (loadedSlot.playerId == user.id) { if (loadedSlot.playerId == user.id) {
loadedSlot.flag = true; loadedSlot.flag = true;
} }
@ -581,14 +590,14 @@ export default class Match {
throw "playerScores was null in a place it really shouldn't have been!"; throw "playerScores was null in a place it really shouldn't have been!";
} }
for (let slot of this.slots) { for (const slot of this.slots) {
// For every empty / locked slot push a null to the data array // For every empty / locked slot push a null to the data array
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {
queryData.push(null); queryData.push(null);
continue; continue;
} }
for (let _playerScore of this.playerScores) { for (const _playerScore of this.playerScores) {
if (_playerScore.player?.id === slot.player?.id && _playerScore._raw !== undefined) { if (_playerScore.player?.id === slot.player?.id && _playerScore._raw !== undefined) {
const score = _playerScore._raw; const score = _playerScore._raw;
queryData.push(`${slot.player?.id}|${score.totalScore}|${score.maxCombo}|${score.count300}|${score.count100}|${score.count50}|${score.countGeki}|${score.countKatu}|${score.countMiss}|${(score.currentHp == 254) ? 1 : 0}${(this.specialModes === 1) ? `|${slot.mods}` : ""}|${score.usingScoreV2 ? 1 : 0}${score.usingScoreV2 ? `|${score.comboPortion}|${score.bonusPortion}` : ""}`); queryData.push(`${slot.player?.id}|${score.totalScore}|${score.maxCombo}|${score.count300}|${score.count100}|${score.count50}|${score.countGeki}|${score.countKatu}|${score.countMiss}|${(score.currentHp == 254) ? 1 : 0}${(this.specialModes === 1) ? `|${slot.mods}` : ""}|${score.usingScoreV2 ? 1 : 0}${score.usingScoreV2 ? `|${score.comboPortion}|${score.bonusPortion}` : ""}`);
@ -623,11 +632,11 @@ export default class Match {
return; return;
} }
matchScoreData.id = user.matchSlot.player.id; matchScoreData.id = user.id;
// Update playerScores // Update playerScores
for (let playerScore of this.playerScores) { for (const playerScore of this.playerScores) {
if (playerScore.player?.id == user.id) { if (playerScore.player?.id === user.id) {
playerScore.score = matchScoreData.totalScore; playerScore.score = matchScoreData.totalScore;
const isCurrentlyFailed = matchScoreData.currentHp == 254; const isCurrentlyFailed = matchScoreData.currentHp == 254;
playerScore.isCurrentlyFailed = isCurrentlyFailed; playerScore.isCurrentlyFailed = isCurrentlyFailed;