Binato/server/MultiplayerManager.ts

169 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

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/packetTypes/MatchJoinData";
import MatchData from "./interfaces/packetTypes/MatchData";
2023-09-10 12:59:22 +01:00
import osu from "../osuTyping";
2023-09-10 18:32:24 +01:00
import TourneyMatchSpecialInfo from "./packets/TourneyMatchSpecialInfo";
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 (const slot of match.slots) {
2022-11-23 00:48:28 +00:00
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
2023-09-10 18:32:24 +01:00
osuPacketWriter.MatchJoinSuccess(match.serialiseMatch());
2022-11-23 00:48:28 +00:00
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 (const match of this.matches.getIterableItems()) {
for (const slot of match.slots) {
2022-11-21 23:26:20 +00:00
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);
}
2023-09-10 18:32:24 +01:00
osuPacketWriter.MatchNew(match.serialiseMatch());
2022-11-21 23:26:20 +00:00
}
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
}
2023-09-10 18:32:24 +01:00
public async LeaveMatch(user:User) {
if (user.match instanceof Match) {
user.match.leaveMatch(user);
let usersInMatch = false;
for (const slot of user.match.slots) {
if (slot.player !== undefined) {
usersInMatch = true;
break;
}
}
if (!usersInMatch) {
this.matches.remove(user.match.matchId.toString());
}
this.UpdateLobbyListing();
}
}
2022-11-17 00:29:07 +00:00
}