2023-09-10 12:59:22 +01:00
|
|
|
import Channel from "./objects/Channel";
|
|
|
|
import Shared from "./objects/Shared";
|
2022-11-21 23:26:20 +00:00
|
|
|
import { SlotStatus } from "./enums/SlotStatus";
|
2023-09-10 12:59:22 +01:00
|
|
|
import DataStream from "./objects/DataStream";
|
|
|
|
import Match from "./objects/Match";
|
|
|
|
import User from "./objects/User";
|
|
|
|
import StatusUpdate from "./packets/StatusUpdate";
|
|
|
|
import UserPresence from "./packets/UserPresence";
|
|
|
|
import UserPresenceBundle from "./packets/UserPresenceBundle";
|
|
|
|
import MatchArray from "./objects/MatchArray";
|
|
|
|
import MatchJoinData from "./interfaces/MatchJoinData";
|
|
|
|
import MatchData from "./interfaces/MatchData";
|
|
|
|
import osu from "../osuTyping";
|
2022-11-19 01:06:03 +00:00
|
|
|
|
2023-09-10 12:59:22 +01:00
|
|
|
export default class MultiplayerManager {
|
2023-08-20 13:03:01 +01:00
|
|
|
private readonly shared:Shared;
|
2022-11-23 00:48:28 +00:00
|
|
|
private matches:MatchArray = new MatchArray();
|
2022-11-19 01:06:03 +00:00
|
|
|
private readonly lobbyStream:DataStream;
|
2022-11-23 00:48:28 +00:00
|
|
|
private readonly lobbyChat:Channel;
|
2022-11-19 01:06:03 +00:00
|
|
|
|
2023-08-20 13:03:01 +01:00
|
|
|
public constructor(shared:Shared) {
|
|
|
|
this.shared = shared;
|
|
|
|
this.lobbyStream = shared.streams.CreateStream("multiplayer:lobby", false);
|
|
|
|
const channel = this.shared.chatManager.GetChannelByName("#lobby");
|
2022-11-23 00:48:28 +00:00
|
|
|
if (channel === undefined) {
|
|
|
|
throw "Something has gone horribly wrong, the lobby channel does not exist!";
|
|
|
|
}
|
|
|
|
this.lobbyChat = channel;
|
2022-11-19 01:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public JoinLobby(user:User) {
|
2022-11-23 00:48:28 +00:00
|
|
|
if (user.inMatch) {
|
|
|
|
user.match?.leaveMatch(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.lobbyChat.Join(user);
|
|
|
|
this.GenerateLobbyListing(user);
|
|
|
|
this.lobbyStream.AddUser(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
public LeaveLobby(user:User) {
|
|
|
|
this.lobbyStream.RemoveUser(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
public JoinMatch(user:User, matchData:number | MatchJoinData) {
|
|
|
|
try {
|
|
|
|
let match:Match | undefined;
|
|
|
|
if (typeof(matchData) === "number") {
|
|
|
|
match = this.matches.getById(matchData);
|
|
|
|
} else {
|
|
|
|
match = this.matches.getById(matchData.matchId);
|
|
|
|
}
|
|
|
|
if (!(match instanceof Match)) {
|
|
|
|
throw "MatchIdInvalid";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match.gamePassword !== undefined && typeof(matchData) !== "number") {
|
|
|
|
if (match.gamePassword !== matchData.gamePassword) {
|
|
|
|
throw "IncorrectPassword";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let matchFull = true;
|
|
|
|
for (let slot of match.slots) {
|
|
|
|
if (slot.player instanceof User || slot.status === SlotStatus.Locked) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
slot.status = SlotStatus.NotReady
|
|
|
|
slot.player = user;
|
|
|
|
user.match = match;
|
|
|
|
user.matchSlot = slot;
|
|
|
|
matchFull = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matchFull) {
|
|
|
|
throw "MatchFull";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inform users in the match that somebody has joined
|
|
|
|
match.sendMatchUpdate();
|
|
|
|
|
|
|
|
match.matchStream.AddUser(user);
|
|
|
|
match.matchChatChannel.Join(user);
|
|
|
|
|
2023-08-20 13:03:01 +01:00
|
|
|
const osuPacketWriter = osu.Bancho.Writer();
|
2022-11-23 00:48:28 +00:00
|
|
|
|
|
|
|
osuPacketWriter.MatchJoinSuccess(match.generateMatchJSON());
|
|
|
|
|
|
|
|
user.addActionToQueue(osuPacketWriter.toBuffer);
|
|
|
|
|
|
|
|
this.UpdateLobbyListing();
|
|
|
|
} catch (e) {
|
2023-08-20 13:03:01 +01:00
|
|
|
const osuPacketWriter = osu.Bancho.Writer();
|
2022-11-23 00:48:28 +00:00
|
|
|
|
|
|
|
osuPacketWriter.MatchJoinFail();
|
|
|
|
|
|
|
|
user.addActionToQueue(osuPacketWriter.toBuffer);
|
|
|
|
|
|
|
|
this.GenerateLobbyListing(user);
|
2022-11-19 01:06:03 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-20 23:37:39 +00:00
|
|
|
|
2022-11-21 23:26:20 +00:00
|
|
|
public UpdateLobbyListing() {
|
|
|
|
this.lobbyStream.Send(this.GenerateLobbyListing());
|
|
|
|
}
|
|
|
|
|
|
|
|
public GenerateLobbyListing(user?:User) : Buffer {
|
2023-08-20 13:03:01 +01:00
|
|
|
const osuPacketWriter = osu.Bancho.Writer();
|
|
|
|
let bufferToSend = UserPresenceBundle(this.shared);
|
2022-11-21 23:26:20 +00:00
|
|
|
|
|
|
|
for (let match of this.matches.getIterableItems()) {
|
|
|
|
for (let slot of match.slots) {
|
|
|
|
if (!(slot.player instanceof User) || slot.status === SlotStatus.Locked) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-08-20 13:03:01 +01:00
|
|
|
const presenceBuffer = UserPresence(this.shared, slot.player.id);
|
|
|
|
const statusBuffer = StatusUpdate(this.shared, slot.player.id);
|
|
|
|
|
|
|
|
if (presenceBuffer === undefined || statusBuffer === undefined) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-11-21 23:26:20 +00:00
|
|
|
bufferToSend = Buffer.concat([bufferToSend, presenceBuffer, statusBuffer], bufferToSend.length + presenceBuffer.length + statusBuffer.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
osuPacketWriter.MatchNew(match.generateMatchJSON());
|
|
|
|
}
|
|
|
|
|
|
|
|
const osuBuffer = osuPacketWriter.toBuffer;
|
|
|
|
bufferToSend = Buffer.concat([bufferToSend, osuBuffer], bufferToSend.length + osuBuffer.length);
|
|
|
|
|
|
|
|
if (user instanceof User) {
|
|
|
|
user.addActionToQueue(bufferToSend);
|
|
|
|
}
|
|
|
|
|
|
|
|
return bufferToSend;
|
|
|
|
}
|
|
|
|
|
2023-08-20 13:03:01 +01:00
|
|
|
public GetMatchById(id:number) : Match | undefined {
|
|
|
|
return this.matches.getById(id);
|
|
|
|
}
|
|
|
|
|
2022-11-20 23:37:39 +00:00
|
|
|
public async CreateMatch(user:User, matchData:MatchData) {
|
2023-08-20 13:03:01 +01:00
|
|
|
const match = await Match.createMatch(user, matchData, this.shared);
|
2022-11-20 23:37:39 +00:00
|
|
|
this.matches.add(match.matchId.toString(), match);
|
2022-11-23 00:48:28 +00:00
|
|
|
this.JoinMatch(user, match.matchId);
|
2022-11-20 23:37:39 +00:00
|
|
|
}
|
2022-11-17 00:29:07 +00:00
|
|
|
}
|