From e297aa11280aaeaab565cffabeeeed33042cef61 Mon Sep 17 00:00:00 2001 From: Holly Date: Sun, 20 Nov 2022 23:37:39 +0000 Subject: [PATCH] multiplayer --- server/BanchoServer.ts | 4 +- server/ChatManager.ts | 18 +++- server/MultiplayerManager.ts | 14 ++- server/Util.ts | 8 ++ server/enums/Mods.ts | 3 + server/objects/Channel.ts | 8 +- server/objects/Match.ts | 166 ++++++++++++++++++++------------- server/objects/Slot.ts | 12 ++- server/packets/StatusUpdate.ts | 2 +- 9 files changed, 159 insertions(+), 76 deletions(-) create mode 100644 server/enums/Mods.ts diff --git a/server/BanchoServer.ts b/server/BanchoServer.ts index 202a509..085311a 100644 --- a/server/BanchoServer.ts +++ b/server/BanchoServer.ts @@ -50,7 +50,7 @@ chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff"); chatManager.AddChatChannel("english", "Talk in exclusively English"); chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese"); -const multiplayerManager:MultiplayerManager = sharedContent.mutiplayerManager = new MultiplayerManager(streams); +const multiplayerManager:MultiplayerManager = sharedContent.mutiplayerManager = new MultiplayerManager(GetSharedContent()); // Add the bot user const botUser:User = users.add("bot", new User(3, "SillyBot", "bot", GetSharedContent())); @@ -198,7 +198,7 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) { break; case Packets.Client_CreateMatch: - //await multiplayerManager.createMultiplayerMatch(PacketUser, CurrentPacket.data); + await multiplayerManager.CreateMatch(PacketUser, CurrentPacket.data); break; case Packets.Client_JoinMatch: diff --git a/server/ChatManager.ts b/server/ChatManager.ts index 0da71b7..c6fb84a 100644 --- a/server/ChatManager.ts +++ b/server/ChatManager.ts @@ -14,7 +14,7 @@ export class ChatManager { this.streams = streams; } - public AddChatChannel(name:string, description:string, forceJoin:boolean = false) { + public AddChatChannel(name:string, description:string, forceJoin:boolean = false) : Channel { const stream = this.streams.CreateStream(`chat_channel:${name}`, false); const channel = new Channel(`#${name}`, description, stream); this.chatChannels.add(channel.name, channel); @@ -22,6 +22,18 @@ export class ChatManager { this.forceJoinChannels.add(name, channel); } ConsoleHelper.printChat(`Created chat channel [${name}]`); + return channel; + } + + public AddSpecialChatChannel(name:string, streamName:string, forceJoin:boolean = false) : Channel { + const stream = this.streams.CreateStream(`chat_channel:${streamName}`, false); + const channel = new Channel(`#${name}`, "", stream); + this.chatChannels.add(channel.name, channel); + if (forceJoin) { + this.forceJoinChannels.add(name, channel); + } + ConsoleHelper.printChat(`Created chat channel [${name}]`); + return channel; } public RemoveChatChannel(channel:Channel | string) { @@ -52,6 +64,10 @@ export class ChatManager { public SendChannelListing(user:User) { const osuPacketWriter = new osu.Bancho.Writer; for (let channel of this.chatChannels.getIterableItems()) { + if (channel.isSpecial) { + continue; + } + osuPacketWriter.ChannelAvailable({ channelName: channel.name, channelTopic: channel.description, diff --git a/server/MultiplayerManager.ts b/server/MultiplayerManager.ts index 2c7d23c..d95aef4 100644 --- a/server/MultiplayerManager.ts +++ b/server/MultiplayerManager.ts @@ -1,15 +1,18 @@ +import { SharedContent } from "./BanchoServer"; import { DataStream } from "./objects/DataStream"; import { DataStreamArray } from "./objects/DataStreamArray"; import { FunkyArray } from "./objects/FunkyArray"; -import { Match } from "./objects/Match"; +import { Match, MatchData } from "./objects/Match"; import { User } from "./objects/User"; export class MultiplayerManager { + private readonly sharedContent:SharedContent; private matches:FunkyArray = new FunkyArray(); private readonly lobbyStream:DataStream; - public constructor(streams:DataStreamArray) { - this.lobbyStream = streams.CreateStream("multiplayer:lobby", false); + public constructor(sharedContent:SharedContent) { + this.sharedContent = sharedContent; + this.lobbyStream = sharedContent.streams.CreateStream("multiplayer:lobby", false); } public JoinLobby(user:User) { @@ -17,4 +20,9 @@ export class MultiplayerManager { } } + + public async CreateMatch(user:User, matchData:MatchData) { + const match = await Match.createMatch(user, matchData, this.sharedContent); + this.matches.add(match.matchId.toString(), match); + } } \ No newline at end of file diff --git a/server/Util.ts b/server/Util.ts index c4995f0..e319fd7 100644 --- a/server/Util.ts +++ b/server/Util.ts @@ -37,4 +37,12 @@ export function hexlify(data:Buffer) : string { } return out.slice(0, out.length - 1); +} + +export function isNullOrEmpty(str:string | undefined | null) { + if (typeof(str) === "string") { + return str !== ""; + } + + return false; } \ No newline at end of file diff --git a/server/enums/Mods.ts b/server/enums/Mods.ts new file mode 100644 index 0000000..b96df8e --- /dev/null +++ b/server/enums/Mods.ts @@ -0,0 +1,3 @@ +export enum Mods { + +} \ No newline at end of file diff --git a/server/objects/Channel.ts b/server/objects/Channel.ts index d6c7895..6252879 100644 --- a/server/objects/Channel.ts +++ b/server/objects/Channel.ts @@ -7,11 +7,17 @@ export class Channel { public description:string; public stream:DataStream; private isLocked:boolean = false; + private _isSpecial:boolean = false; - public constructor(name:string, description:string, stream:DataStream) { + public constructor(name:string, description:string, stream:DataStream, isSpecial:boolean = false) { this.name = name; this.description = description; this.stream = stream; + this._isSpecial = isSpecial; + } + + public get isSpecial() { + return this._isSpecial; } public get userCount() { diff --git a/server/objects/Match.ts b/server/objects/Match.ts index 2a90ca4..61f9eb7 100644 --- a/server/objects/Match.ts +++ b/server/objects/Match.ts @@ -1,103 +1,135 @@ +import { Channel } from "./Channel"; +import { SharedContent } from "../BanchoServer"; +import { DataStream } from "./DataStream"; +import { Slot } from "./Slot"; +import { User } from "./User"; +import { StatusUpdate } from "../packets/StatusUpdate"; + const osu = require("osu-packet"); +export interface MatchData { + matchId:number, + matchType:number, + activeMods:number, + gameName:string, + gamePassword:string, + inProgress:boolean, + beatmapName:string, + beatmapId:number, + beatmapChecksum:string, + slots:Array, + host:number, + playMode:number, + matchScoringType:number, + matchTeamType:number, + specialModes:number, + seed:number +} + export class Match { - /*public matchId:number = -1; + // osu! Data + public matchId:number = -1; public inProgress:boolean = false; - public matchType: 0; - public activeMods: 0; - public gameName: ""; - public gamePassword: ''; - public beatmapName: ''; - public beatmapId: 0; - public beatmapChecksum: ''; - public slots: []; + public matchType:number = 0; + public activeMods:number = 0; + public gameName:string = ""; + public gamePassword:string | undefined = ''; + public beatmapName:string = ''; + public beatmapId:number = 0; + public beatmapChecksum:string = ''; + public slots:Array = new Array(); public host:number = 0; public playMode:number = 0; public matchScoringType:number = 0; public matchTeamType:number = 0; public specialModes:number = 0; - public seed:number = 0; + public seed:number = 0; - constructor(MatchData = {}) { - this.matchId = MatchData.matchId; + // Binato data + public roundId:number = 0; + public matchStartCountdownActive:boolean = false; + public matchStream:DataStream; + public matchChatChannel:Channel; - this.roundId = 0; + private constructor(matchData:MatchData, sharedContent:SharedContent) { + console.log(matchData); + this.matchId = matchData.matchId; - this.inProgress = MatchData.inProgress; - this.matchStartCountdownActive = false; + this.inProgress = matchData.inProgress; - this.matchType = MatchData.matchType; + this.matchType = matchData.matchType; - this.activeMods = MatchData.activeMods; + this.activeMods = matchData.activeMods; - this.gameName = MatchData.gameName; - if (MatchData.gamePassword == '') MatchData.gamePassword == null; - this.gamePassword = MatchData.gamePassword; + this.gameName = matchData.gameName; + if (matchData.gamePassword == '') matchData.gamePassword == null; + this.gamePassword = matchData.gamePassword; - this.beatmapName = MatchData.beatmapName; - this.beatmapId = MatchData.beatmapId; - this.beatmapChecksum = MatchData.beatmapChecksum; + this.beatmapName = matchData.beatmapName; + this.beatmapId = matchData.beatmapId; + this.beatmapChecksum = matchData.beatmapChecksum; - this.slots = MatchData.slots; + this.slots = matchData.slots; for (let i = 0; i < this.slots.length; i++) { - this.slots[i].mods = 0; + //this.slots[i].mods = 0; } - this.host = MatchData.host; + this.host = matchData.host; - this.playMode = MatchData.playMode; + this.playMode = matchData.playMode; - this.matchScoringType = MatchData.matchScoringType; - this.matchTeamType = MatchData.matchTeamType; - this.specialModes = MatchData.specialModes; + this.matchScoringType = matchData.matchScoringType; + this.matchTeamType = matchData.matchTeamType; + this.specialModes = matchData.specialModes; - this.seed = MatchData.seed; + this.seed = matchData.seed; - this.matchStreamName = `mp_${this.matchId}`; - this.matchChatStreamName = `mp_chat_${this.matchId}`; + this.matchStream = sharedContent.streams.CreateStream(`multiplayer:match_${this.matchId}`); + this.matchChatChannel = sharedContent.chatManager.AddSpecialChatChannel("multiplayer", `mp_${this.matchId}`); - this.matchLoadSlots = null; - this.matchSkippedSlots = null; + //this.matchLoadSlots = null; + //this.matchSkippedSlots = null; - this.playerScores = null; + //this.playerScores = null; - this.multiplayerExtras = null; + //this.multiplayerExtras = null; - this.isTourneyMatch = false; - this.tourneyClientUsers = []; + //this.isTourneyMatch = false; + //this.tourneyClientUsers = []; } - static createMatch(MatchHost = new User, MatchData = {matchId: -1,inProgress: false,matchType: 0,activeMods: 0,gameName: "",gamePassword: '',beatmapName: '',beatmapId: 0,beatmapChecksum: '',slots: [],host: 0,playMode: 0,matchScoringType: 0,matchTeamType: 0,specialModes: 0,seed: 0}) { - return new Promise(async (resolve, reject) => { - MatchData.matchId = (await global.DatabaseHelper.query( - "INSERT INTO mp_matches (id, name, open_time, close_time, seed) VALUES (NULL, ?, UNIX_TIMESTAMP(), NULL, ?) RETURNING id;", - [MatchData.gameName, MatchData.seed] - ))[0]["id"]; - - const matchInstance = new MultiplayerMatch(MatchData); - - console.log(matchInstance.matchId); - - // Update the status of the current user - StatusUpdate(MatchHost, MatchHost.id); - - const osuPacketWriter = new osu.Bancho.Writer; - - osuPacketWriter.MatchNew(matchInstance.createOsuMatchJSON()); - - MatchHost.addActionToQueue(osuPacketWriter.toBuffer); - - Streams.addStream(matchInstance.matchStreamName, true, matchInstance.matchId); - Streams.addStream(matchInstance.matchChatStreamName, true, matchInstance.matchId); - - // Update the match listing for users in the multiplayer lobby - global.MultiplayerManager.updateMatchListing(); - - resolve(matchInstance); + public static createMatch(matchHost:User, matchData:MatchData, sharedContent:SharedContent) : Promise { + return new Promise(async (resolve, reject) => { + try { + matchData.matchId = (await sharedContent.database.query( + "INSERT INTO mp_matches (id, name, open_time, close_time, seed) VALUES (NULL, ?, UNIX_TIMESTAMP(), NULL, ?) RETURNING id;", + [matchData.gameName, matchData.seed] + ))[0]["id"]; + + const matchInstance = new Match(matchData, sharedContent); + + console.log(matchInstance.matchId); + + // Update the status of the current user + StatusUpdate(matchHost, matchHost.id); + + const osuPacketWriter = new osu.Bancho.Writer; + + //osuPacketWriter.MatchNew(matchInstance.createOsuMatchJSON()); + + matchHost.addActionToQueue(osuPacketWriter.toBuffer); + + // Update the match listing for users in the multiplayer lobby + //global.MultiplayerManager.updateMatchListing(); + + resolve(matchInstance); + } catch (e) { + reject(e); + } }); } - getSlotIdByPlayerId(playerId = 0) { + /*getSlotIdByPlayerId(playerId = 0) { const player = getUserById(playerId); if (player != null) return player.matchSlotId; diff --git a/server/objects/Slot.ts b/server/objects/Slot.ts index 6af715b..39b5405 100644 --- a/server/objects/Slot.ts +++ b/server/objects/Slot.ts @@ -1,3 +1,13 @@ export class Slot { - + public status:number; + public team:number; + public player:number; // playerId + public mods:number; + + public constructor() { + this.status = 0; + this.team = 0; + this.player = 0; + this.mods = 0; + } } \ No newline at end of file diff --git a/server/packets/StatusUpdate.ts b/server/packets/StatusUpdate.ts index a4d6a23..f6ba123 100644 --- a/server/packets/StatusUpdate.ts +++ b/server/packets/StatusUpdate.ts @@ -2,7 +2,7 @@ import { RankingModes } from "../enums/RankingModes"; import { User } from "../objects/User"; const osu = require("osu-packet"); -export function StatusUpdate(user:User, id:number, sendImmidiate:boolean = false) { +export function StatusUpdate(user:User, id:number, sendImmidiate:boolean = true) { if (id == 3) return; // Ignore Bot // Create new osu packet writer