more multiplayer stuff
This commit is contained in:
parent
e297aa1128
commit
be52b19002
9 changed files with 186 additions and 78 deletions
|
@ -1,9 +1,14 @@
|
|||
import { SharedContent } from "./BanchoServer";
|
||||
import { SlotStatus } from "./enums/SlotStatus";
|
||||
import { DataStream } from "./objects/DataStream";
|
||||
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||
import { FunkyArray } from "./objects/FunkyArray";
|
||||
import { Match, MatchData } from "./objects/Match";
|
||||
import { User } from "./objects/User";
|
||||
import { StatusUpdate } from "./packets/StatusUpdate";
|
||||
import { UserPresence } from "./packets/UserPresence";
|
||||
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export class MultiplayerManager {
|
||||
private readonly sharedContent:SharedContent;
|
||||
|
@ -21,6 +26,38 @@ export class MultiplayerManager {
|
|||
}
|
||||
}
|
||||
|
||||
public UpdateLobbyListing() {
|
||||
this.lobbyStream.Send(this.GenerateLobbyListing());
|
||||
}
|
||||
|
||||
public GenerateLobbyListing(user?:User) : Buffer {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
let bufferToSend = UserPresenceBundle(this.sharedContent);
|
||||
|
||||
for (let match of this.matches.getIterableItems()) {
|
||||
for (let slot of match.slots) {
|
||||
if (!(slot.player instanceof User) || slot.status === SlotStatus.Locked) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const presenceBuffer = UserPresence(this.sharedContent, slot.player.id);
|
||||
const statusBuffer = StatusUpdate(this.sharedContent, slot.player.id);
|
||||
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;
|
||||
}
|
||||
|
||||
public async CreateMatch(user:User, matchData:MatchData) {
|
||||
const match = await Match.createMatch(user, matchData, this.sharedContent);
|
||||
this.matches.add(match.matchId.toString(), match);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// TODO: Mods enum
|
||||
export enum Mods {
|
||||
|
||||
}
|
6
server/enums/SlotStatus.ts
Normal file
6
server/enums/SlotStatus.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export enum SlotStatus {
|
||||
Unknown_0,
|
||||
Empty,
|
||||
Locked,
|
||||
NotReady,
|
||||
}
|
|
@ -17,7 +17,7 @@ export interface MatchData {
|
|||
beatmapName:string,
|
||||
beatmapId:number,
|
||||
beatmapChecksum:string,
|
||||
slots:Array<any>,
|
||||
slots:Array<MatchDataSlot>,
|
||||
host:number,
|
||||
playMode:number,
|
||||
matchScoringType:number,
|
||||
|
@ -26,6 +26,13 @@ export interface MatchData {
|
|||
seed:number
|
||||
}
|
||||
|
||||
export interface MatchDataSlot {
|
||||
status:number,
|
||||
team:number,
|
||||
playerId:number,
|
||||
mods:number | undefined,
|
||||
}
|
||||
|
||||
export class Match {
|
||||
// osu! Data
|
||||
public matchId:number = -1;
|
||||
|
@ -33,12 +40,12 @@ export class Match {
|
|||
public matchType:number = 0;
|
||||
public activeMods:number = 0;
|
||||
public gameName:string = "";
|
||||
public gamePassword:string | undefined = '';
|
||||
public gamePassword?:string;
|
||||
public beatmapName:string = '';
|
||||
public beatmapId:number = 0;
|
||||
public beatmapChecksum:string = '';
|
||||
public slots:Array<Slot> = new Array<Slot>();
|
||||
public host:number = 0;
|
||||
public host:User;
|
||||
public playMode:number = 0;
|
||||
public matchScoringType:number = 0;
|
||||
public matchTeamType:number = 0;
|
||||
|
@ -51,7 +58,11 @@ export class Match {
|
|||
public matchStream:DataStream;
|
||||
public matchChatChannel:Channel;
|
||||
|
||||
private cachedMatchJSON:MatchData;
|
||||
private readonly sharedContent:SharedContent;
|
||||
|
||||
private constructor(matchData:MatchData, sharedContent:SharedContent) {
|
||||
this.sharedContent = sharedContent;
|
||||
console.log(matchData);
|
||||
this.matchId = matchData.matchId;
|
||||
|
||||
|
@ -69,12 +80,21 @@ export class Match {
|
|||
this.beatmapId = matchData.beatmapId;
|
||||
this.beatmapChecksum = matchData.beatmapChecksum;
|
||||
|
||||
this.slots = matchData.slots;
|
||||
for (let i = 0; i < this.slots.length; i++) {
|
||||
//this.slots[i].mods = 0;
|
||||
for (let slot of matchData.slots) {
|
||||
if (slot.playerId === -1) {
|
||||
this.slots.push(new Slot(slot.status, slot.team, undefined, slot.mods));
|
||||
} else {
|
||||
this.slots.push(new Slot(slot.status, slot.team, sharedContent.users.getById(slot.playerId), slot.mods));
|
||||
}
|
||||
}
|
||||
|
||||
this.host = matchData.host;
|
||||
const hostUser = sharedContent.users.getById(matchData.host);
|
||||
if (hostUser === undefined) {
|
||||
// NOTE: This should never be possible to hit
|
||||
// since this user JUST made the match.
|
||||
throw "Host User of match was undefined";
|
||||
}
|
||||
this.host = hostUser;
|
||||
|
||||
this.playMode = matchData.playMode;
|
||||
|
||||
|
@ -87,6 +107,8 @@ export class Match {
|
|||
this.matchStream = sharedContent.streams.CreateStream(`multiplayer:match_${this.matchId}`);
|
||||
this.matchChatChannel = sharedContent.chatManager.AddSpecialChatChannel("multiplayer", `mp_${this.matchId}`);
|
||||
|
||||
this.cachedMatchJSON = matchData;
|
||||
|
||||
//this.matchLoadSlots = null;
|
||||
//this.matchSkippedSlots = null;
|
||||
|
||||
|
@ -115,7 +137,7 @@ export class Match {
|
|||
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
|
||||
//osuPacketWriter.MatchNew(matchInstance.createOsuMatchJSON());
|
||||
osuPacketWriter.MatchNew(matchInstance.generateMatchJSON());
|
||||
|
||||
matchHost.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
|
||||
|
@ -134,30 +156,28 @@ export class Match {
|
|||
|
||||
if (player != null) return player.matchSlotId;
|
||||
else return null;
|
||||
}*/
|
||||
|
||||
// Convert class data back to a format that osu-packet can understand
|
||||
generateMatchJSON() : MatchData {
|
||||
for (let i = 0; i < this.slots.length; i++) {
|
||||
const slot = this.slots[i];
|
||||
const osuSlot = this.cachedMatchJSON.slots[i];
|
||||
|
||||
osuSlot.status = slot.status;
|
||||
osuSlot.team = slot.team;
|
||||
osuSlot.mods = slot.mods;
|
||||
if (slot.player instanceof User) {
|
||||
osuSlot.playerId = slot.player.id;
|
||||
} else {
|
||||
osuSlot.playerId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
createOsuMatchJSON() {
|
||||
return {
|
||||
matchId: this.matchId,
|
||||
inProgress: this.inProgress,
|
||||
matchType: this.matchType,
|
||||
activeMods: this.activeMods,
|
||||
gameName: this.gameName,
|
||||
gamePassword: this.gamePassword,
|
||||
beatmapName: this.beatmapName,
|
||||
beatmapId: this.beatmapId,
|
||||
beatmapChecksum: this.beatmapChecksum,
|
||||
slots: this.slots,
|
||||
host: this.host,
|
||||
playMode: this.playMode,
|
||||
matchScoringType: this.matchScoringType,
|
||||
matchTeamType: this.matchTeamType,
|
||||
specialModes: this.specialModes,
|
||||
seed: this.seed
|
||||
};
|
||||
return this.cachedMatchJSON;
|
||||
}
|
||||
|
||||
leaveMatch(MatchUser = new User) {
|
||||
/*leaveMatch(MatchUser = new User) {
|
||||
// Make sure this leave call is valid
|
||||
if (!MatchUser.inMatch) return;
|
||||
|
||||
|
@ -181,67 +201,78 @@ export class Match {
|
|||
osuPacketWriter.ChannelRevoked("#multiplayer");
|
||||
|
||||
MatchUser.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}*/
|
||||
|
||||
async updateMatch(user:User, matchData:MatchData) {
|
||||
// Update match with new data
|
||||
this.inProgress = matchData.inProgress;
|
||||
|
||||
this.matchType = matchData.matchType;
|
||||
|
||||
this.activeMods = matchData.activeMods;
|
||||
|
||||
const gameNameChanged = this.gameName !== matchData.gameName;
|
||||
this.gameName = matchData.gameName;
|
||||
|
||||
if (matchData.gamePassword === "") {
|
||||
this.gamePassword = undefined;
|
||||
} else {
|
||||
this.gamePassword = this.cachedMatchJSON.gamePassword = matchData.gamePassword;
|
||||
}
|
||||
|
||||
async updateMatch(MatchUser = new User, MatchData) {
|
||||
// Update match with new data
|
||||
this.inProgress = MatchData.inProgress;
|
||||
this.beatmapName = this.cachedMatchJSON.beatmapName = matchData.beatmapName;
|
||||
this.beatmapId = this.cachedMatchJSON.beatmapId = matchData.beatmapId;
|
||||
this.beatmapChecksum = this.cachedMatchJSON.beatmapChecksum = matchData.beatmapChecksum;
|
||||
|
||||
this.matchType = MatchData.matchType;
|
||||
if (matchData.host !== this.host.id) {
|
||||
const hostUser = this.sharedContent.users.getById(matchData.host);
|
||||
if (hostUser === undefined) {
|
||||
// NOTE: This should never be possible to hit
|
||||
// since this user JUST made the match.
|
||||
throw "Host User of match was undefined";
|
||||
}
|
||||
this.host = hostUser;
|
||||
this.cachedMatchJSON.host = this.host.id;
|
||||
}
|
||||
|
||||
this.activeMods = MatchData.activeMods;
|
||||
this.playMode = matchData.playMode;
|
||||
|
||||
const gameNameChanged = this.gameName !== MatchData.gameName;
|
||||
this.gameName = MatchData.gameName;
|
||||
this.matchScoringType = matchData.matchScoringType;
|
||||
this.matchTeamType = matchData.matchTeamType;
|
||||
this.specialModes = matchData.specialModes;
|
||||
|
||||
if (MatchData.gamePassword == '') MatchData.gamePassword == null;
|
||||
this.gamePassword = MatchData.gamePassword;
|
||||
|
||||
this.beatmapName = MatchData.beatmapName;
|
||||
this.beatmapId = MatchData.beatmapId;
|
||||
this.beatmapChecksum = MatchData.beatmapChecksum;
|
||||
|
||||
this.host = MatchData.host;
|
||||
|
||||
this.playMode = MatchData.playMode;
|
||||
|
||||
this.matchScoringType = MatchData.matchScoringType;
|
||||
this.matchTeamType = MatchData.matchTeamType;
|
||||
this.specialModes = MatchData.specialModes;
|
||||
|
||||
const gameSeedChanged = this.seed !== MatchData.seed;
|
||||
this.seed = MatchData.seed;
|
||||
const gameSeedChanged = this.seed !== matchData.seed;
|
||||
this.seed = matchData.seed;
|
||||
|
||||
if (gameNameChanged || gameSeedChanged) {
|
||||
const queryData = [];
|
||||
if (gameNameChanged) {
|
||||
queryData.push(MatchData.gameName);
|
||||
queryData.push(matchData.gameName);
|
||||
}
|
||||
if (gameSeedChanged) {
|
||||
queryData.push(MatchData.seed);
|
||||
queryData.push(matchData.seed);
|
||||
}
|
||||
queryData.push(this.matchId);
|
||||
|
||||
await global.DatabaseHelper.query(`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`, queryData);
|
||||
await this.sharedContent.database.query(`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`, queryData);
|
||||
}
|
||||
|
||||
this.sendMatchUpdate();
|
||||
|
||||
// Update the match listing in the lobby to reflect these changes
|
||||
global.MultiplayerManager.updateMatchListing();
|
||||
//global.MultiplayerManager.updateMatchListing();
|
||||
}
|
||||
|
||||
sendMatchUpdate() {
|
||||
public sendMatchUpdate() {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
|
||||
osuPacketWriter.MatchUpdate(this.createOsuMatchJSON());
|
||||
osuPacketWriter.MatchUpdate(this.generateMatchJSON());
|
||||
|
||||
// Update all users in the match with new match information
|
||||
if (Streams.exists(this.matchStreamName))
|
||||
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||
this.matchStream.Send(osuPacketWriter.toBuffer);
|
||||
}
|
||||
|
||||
moveToSlot(MatchUser = new User, SlotToMoveTo) {
|
||||
/*moveToSlot(MatchUser = new User, SlotToMoveTo) {
|
||||
const oldSlot = this.slots[MatchUser.matchSlotId];
|
||||
|
||||
// Set the new slot's data to the user's old slot data
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { SlotStatus } from "../enums/SlotStatus";
|
||||
import { User } from "./User";
|
||||
|
||||
export class Slot {
|
||||
public status:number;
|
||||
public status:SlotStatus;
|
||||
public team:number;
|
||||
public player:number; // playerId
|
||||
public player?:User; // playerId
|
||||
public mods:number;
|
||||
|
||||
public constructor() {
|
||||
this.status = 0;
|
||||
this.team = 0;
|
||||
this.player = 0;
|
||||
this.mods = 0;
|
||||
public constructor(status:SlotStatus, team:number, player?:User, mods:number = 0) {
|
||||
this.status = status;
|
||||
this.team = team;
|
||||
this.player = player;
|
||||
this.mods = mods;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ export function ChangeAction(user:User, data:any) {
|
|||
user.updatePresence(data);
|
||||
|
||||
if (user.spectatorStream != null) {
|
||||
const statusUpdate = StatusUpdate(user, user.id, false);
|
||||
const statusUpdate = StatusUpdate(user.sharedContent, user.id);
|
||||
user.spectatorStream.Send(statusUpdate);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,22 @@
|
|||
import { SharedContent } from "../BanchoServer";
|
||||
import { RankingModes } from "../enums/RankingModes";
|
||||
import { User } from "../objects/User";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export function StatusUpdate(user:User, id:number, sendImmidiate:boolean = true) {
|
||||
export function StatusUpdate(arg0:User | SharedContent, id:number) {
|
||||
if (id == 3) return; // Ignore Bot
|
||||
|
||||
// Create new osu packet writer
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
let sharedContent:SharedContent;
|
||||
if (arg0 instanceof User) {
|
||||
sharedContent = arg0.sharedContent;
|
||||
} else {
|
||||
sharedContent = arg0;
|
||||
}
|
||||
|
||||
// Get user's class
|
||||
const userData = user.sharedContent.users.getById(id);
|
||||
const userData = sharedContent.users.getById(id);
|
||||
|
||||
if (userData == null) return;
|
||||
|
||||
|
@ -32,6 +39,9 @@ export function StatusUpdate(user:User, id:number, sendImmidiate:boolean = true)
|
|||
osuPacketWriter.HandleOsuUpdate(UserStatusObject);
|
||||
|
||||
// Send data to user's queue
|
||||
if (sendImmidiate) user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
else return osuPacketWriter.toBuffer;
|
||||
if (arg0 instanceof User) {
|
||||
arg0.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}
|
||||
|
||||
return osuPacketWriter.toBuffer;
|
||||
}
|
|
@ -1,10 +1,17 @@
|
|||
import { SharedContent } from "../BanchoServer";
|
||||
import { User } from "../objects/User";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export function UserPresence(user:User, id:number, sendImmidiate:boolean = true) {
|
||||
export function UserPresence(arg0:User | SharedContent, id:number) {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
let sharedContent:SharedContent;
|
||||
if (arg0 instanceof User) {
|
||||
sharedContent = arg0.sharedContent;
|
||||
} else {
|
||||
sharedContent = arg0;
|
||||
}
|
||||
|
||||
const userData = user.sharedContent.users.getById(id);
|
||||
const userData = sharedContent.users.getById(id);
|
||||
|
||||
if (userData == null) return;
|
||||
|
||||
|
@ -19,6 +26,9 @@ export function UserPresence(user:User, id:number, sendImmidiate:boolean = true)
|
|||
rank: userData.rank
|
||||
});
|
||||
|
||||
if (sendImmidiate) userData.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
else return osuPacketWriter.toBuffer;
|
||||
if (arg0 instanceof User) {
|
||||
arg0.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}
|
||||
|
||||
return osuPacketWriter.toBuffer;
|
||||
}
|
|
@ -1,17 +1,27 @@
|
|||
import { SharedContent } from "../BanchoServer";
|
||||
import { User } from "../objects/User";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export function UserPresenceBundle(user:User, sendImmidiate:boolean = true) {
|
||||
export function UserPresenceBundle(arg0:User | SharedContent) : Buffer {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
let sharedContent:SharedContent;
|
||||
if (arg0 instanceof User) {
|
||||
sharedContent = arg0.sharedContent;
|
||||
} else {
|
||||
sharedContent = arg0;
|
||||
}
|
||||
|
||||
let userIds:Array<number> = new Array<number>();
|
||||
|
||||
for (let userData of user.sharedContent.users.getIterableItems()) {
|
||||
for (let userData of sharedContent.users.getIterableItems()) {
|
||||
userIds.push(userData.id);
|
||||
}
|
||||
|
||||
osuPacketWriter.UserPresenceBundle(userIds);
|
||||
|
||||
if (sendImmidiate) user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
else return osuPacketWriter.toBuffer;
|
||||
if (arg0 instanceof User) {
|
||||
arg0.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}
|
||||
|
||||
return osuPacketWriter.toBuffer;
|
||||
}
|
Loading…
Reference in a new issue