multiplayer

This commit is contained in:
Holly Stubbs 2022-11-20 23:37:39 +00:00
parent f7f2df1287
commit e297aa1128
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
9 changed files with 159 additions and 76 deletions

View file

@ -50,7 +50,7 @@ chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff");
chatManager.AddChatChannel("english", "Talk in exclusively English"); chatManager.AddChatChannel("english", "Talk in exclusively English");
chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese"); 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 // Add the bot user
const botUser:User = users.add("bot", new User(3, "SillyBot", "bot", GetSharedContent())); 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; break;
case Packets.Client_CreateMatch: case Packets.Client_CreateMatch:
//await multiplayerManager.createMultiplayerMatch(PacketUser, CurrentPacket.data); await multiplayerManager.CreateMatch(PacketUser, CurrentPacket.data);
break; break;
case Packets.Client_JoinMatch: case Packets.Client_JoinMatch:

View file

@ -14,7 +14,7 @@ export class ChatManager {
this.streams = streams; 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 stream = this.streams.CreateStream(`chat_channel:${name}`, false);
const channel = new Channel(`#${name}`, description, stream); const channel = new Channel(`#${name}`, description, stream);
this.chatChannels.add(channel.name, channel); this.chatChannels.add(channel.name, channel);
@ -22,6 +22,18 @@ export class ChatManager {
this.forceJoinChannels.add(name, channel); this.forceJoinChannels.add(name, channel);
} }
ConsoleHelper.printChat(`Created chat channel [${name}]`); 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) { public RemoveChatChannel(channel:Channel | string) {
@ -52,6 +64,10 @@ export class ChatManager {
public SendChannelListing(user:User) { public SendChannelListing(user:User) {
const osuPacketWriter = new osu.Bancho.Writer; const osuPacketWriter = new osu.Bancho.Writer;
for (let channel of this.chatChannels.getIterableItems()) { for (let channel of this.chatChannels.getIterableItems()) {
if (channel.isSpecial) {
continue;
}
osuPacketWriter.ChannelAvailable({ osuPacketWriter.ChannelAvailable({
channelName: channel.name, channelName: channel.name,
channelTopic: channel.description, channelTopic: channel.description,

View file

@ -1,15 +1,18 @@
import { SharedContent } from "./BanchoServer";
import { DataStream } from "./objects/DataStream"; import { DataStream } from "./objects/DataStream";
import { DataStreamArray } from "./objects/DataStreamArray"; import { DataStreamArray } from "./objects/DataStreamArray";
import { FunkyArray } from "./objects/FunkyArray"; import { FunkyArray } from "./objects/FunkyArray";
import { Match } from "./objects/Match"; import { Match, MatchData } from "./objects/Match";
import { User } from "./objects/User"; import { User } from "./objects/User";
export class MultiplayerManager { export class MultiplayerManager {
private readonly sharedContent:SharedContent;
private matches:FunkyArray<Match> = new FunkyArray<Match>(); private matches:FunkyArray<Match> = new FunkyArray<Match>();
private readonly lobbyStream:DataStream; private readonly lobbyStream:DataStream;
public constructor(streams:DataStreamArray) { public constructor(sharedContent:SharedContent) {
this.lobbyStream = streams.CreateStream("multiplayer:lobby", false); this.sharedContent = sharedContent;
this.lobbyStream = sharedContent.streams.CreateStream("multiplayer:lobby", false);
} }
public JoinLobby(user:User) { 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);
}
} }

View file

@ -38,3 +38,11 @@ export function hexlify(data:Buffer) : string {
return out.slice(0, out.length - 1); return out.slice(0, out.length - 1);
} }
export function isNullOrEmpty(str:string | undefined | null) {
if (typeof(str) === "string") {
return str !== "";
}
return false;
}

3
server/enums/Mods.ts Normal file
View file

@ -0,0 +1,3 @@
export enum Mods {
}

View file

@ -7,11 +7,17 @@ export class Channel {
public description:string; public description:string;
public stream:DataStream; public stream:DataStream;
private isLocked:boolean = false; 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.name = name;
this.description = description; this.description = description;
this.stream = stream; this.stream = stream;
this._isSpecial = isSpecial;
}
public get isSpecial() {
return this._isSpecial;
} }
public get userCount() { public get userCount() {

View file

@ -1,16 +1,43 @@
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"); 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<any>,
host:number,
playMode:number,
matchScoringType:number,
matchTeamType:number,
specialModes:number,
seed:number
}
export class Match { export class Match {
/*public matchId:number = -1; // osu! Data
public matchId:number = -1;
public inProgress:boolean = false; public inProgress:boolean = false;
public matchType: 0; public matchType:number = 0;
public activeMods: 0; public activeMods:number = 0;
public gameName: ""; public gameName:string = "";
public gamePassword: ''; public gamePassword:string | undefined = '';
public beatmapName: ''; public beatmapName:string = '';
public beatmapId: 0; public beatmapId:number = 0;
public beatmapChecksum: ''; public beatmapChecksum:string = '';
public slots: []; public slots:Array<Slot> = new Array<Slot>();
public host:number = 0; public host:number = 0;
public playMode:number = 0; public playMode:number = 0;
public matchScoringType:number = 0; public matchScoringType:number = 0;
@ -18,86 +45,91 @@ export class Match {
public specialModes:number = 0; public specialModes:number = 0;
public seed:number = 0; public seed:number = 0;
constructor(MatchData = {}) { // Binato data
this.matchId = MatchData.matchId; 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.inProgress = matchData.inProgress;
this.matchStartCountdownActive = false;
this.matchType = MatchData.matchType; this.matchType = matchData.matchType;
this.activeMods = MatchData.activeMods; this.activeMods = matchData.activeMods;
this.gameName = MatchData.gameName; this.gameName = matchData.gameName;
if (MatchData.gamePassword == '') MatchData.gamePassword == null; if (matchData.gamePassword == '') matchData.gamePassword == null;
this.gamePassword = MatchData.gamePassword; this.gamePassword = matchData.gamePassword;
this.beatmapName = MatchData.beatmapName; this.beatmapName = matchData.beatmapName;
this.beatmapId = MatchData.beatmapId; this.beatmapId = matchData.beatmapId;
this.beatmapChecksum = MatchData.beatmapChecksum; this.beatmapChecksum = matchData.beatmapChecksum;
this.slots = MatchData.slots; this.slots = matchData.slots;
for (let i = 0; i < this.slots.length; i++) { 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.matchScoringType = matchData.matchScoringType;
this.matchTeamType = MatchData.matchTeamType; this.matchTeamType = matchData.matchTeamType;
this.specialModes = MatchData.specialModes; this.specialModes = matchData.specialModes;
this.seed = MatchData.seed; this.seed = matchData.seed;
this.matchStreamName = `mp_${this.matchId}`; this.matchStream = sharedContent.streams.CreateStream(`multiplayer:match_${this.matchId}`);
this.matchChatStreamName = `mp_chat_${this.matchId}`; this.matchChatChannel = sharedContent.chatManager.AddSpecialChatChannel("multiplayer", `mp_${this.matchId}`);
this.matchLoadSlots = null; //this.matchLoadSlots = null;
this.matchSkippedSlots = null; //this.matchSkippedSlots = null;
this.playerScores = null; //this.playerScores = null;
this.multiplayerExtras = null; //this.multiplayerExtras = null;
this.isTourneyMatch = false; //this.isTourneyMatch = false;
this.tourneyClientUsers = []; //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}) { public static createMatch(matchHost:User, matchData:MatchData, sharedContent:SharedContent) : Promise<Match> {
return new Promise(async (resolve, reject) => { return new Promise<Match>(async (resolve, reject) => {
MatchData.matchId = (await global.DatabaseHelper.query( 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;", "INSERT INTO mp_matches (id, name, open_time, close_time, seed) VALUES (NULL, ?, UNIX_TIMESTAMP(), NULL, ?) RETURNING id;",
[MatchData.gameName, MatchData.seed] [matchData.gameName, matchData.seed]
))[0]["id"]; ))[0]["id"];
const matchInstance = new MultiplayerMatch(MatchData); const matchInstance = new Match(matchData, sharedContent);
console.log(matchInstance.matchId); console.log(matchInstance.matchId);
// Update the status of the current user // Update the status of the current user
StatusUpdate(MatchHost, MatchHost.id); StatusUpdate(matchHost, matchHost.id);
const osuPacketWriter = new osu.Bancho.Writer; const osuPacketWriter = new osu.Bancho.Writer;
osuPacketWriter.MatchNew(matchInstance.createOsuMatchJSON()); //osuPacketWriter.MatchNew(matchInstance.createOsuMatchJSON());
MatchHost.addActionToQueue(osuPacketWriter.toBuffer); 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 // Update the match listing for users in the multiplayer lobby
global.MultiplayerManager.updateMatchListing(); //global.MultiplayerManager.updateMatchListing();
resolve(matchInstance); resolve(matchInstance);
} catch (e) {
reject(e);
}
}); });
} }
getSlotIdByPlayerId(playerId = 0) { /*getSlotIdByPlayerId(playerId = 0) {
const player = getUserById(playerId); const player = getUserById(playerId);
if (player != null) return player.matchSlotId; if (player != null) return player.matchSlotId;

View file

@ -1,3 +1,13 @@
export class Slot { 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;
}
} }

View file

@ -2,7 +2,7 @@ import { RankingModes } from "../enums/RankingModes";
import { User } from "../objects/User"; import { User } from "../objects/User";
const osu = require("osu-packet"); 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 if (id == 3) return; // Ignore Bot
// Create new osu packet writer // Create new osu packet writer