basic functionality
This commit is contained in:
parent
5ed106b7d4
commit
2beeb5fd09
22 changed files with 945 additions and 103 deletions
20
Binato.ts
20
Binato.ts
|
@ -1,14 +1,24 @@
|
||||||
|
console.clear();
|
||||||
|
|
||||||
|
import { ConsoleHelper } from "./ConsoleHelper";
|
||||||
|
import { readFileSync, existsSync } from "fs";
|
||||||
|
if (!existsSync("./config.json")) {
|
||||||
|
ConsoleHelper.printError("You must have a config file in the root of Binato's folder structure.");
|
||||||
|
ConsoleHelper.printError("Check the GitHub for an example file");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
import { ChatHistory } from "./server/ChatHistory";
|
import { ChatHistory } from "./server/ChatHistory";
|
||||||
import compression from "compression";
|
import compression from "compression";
|
||||||
import { ConsoleHelper } from "./ConsoleHelper";
|
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { HandleRequest } from "./server/BanchoServer";
|
import { HandleRequest } from "./server/BanchoServer";
|
||||||
import { readFileSync } from "fs";
|
|
||||||
import { Registry, collectDefaultMetrics } from "prom-client";
|
import { Registry, collectDefaultMetrics } from "prom-client";
|
||||||
const config:any = JSON.parse(readFileSync(__dirname + "/config.json").toString());
|
const config:any = JSON.parse(readFileSync(__dirname + "/config.json").toString());
|
||||||
|
|
||||||
const binatoApp:express.Application = express();
|
const binatoApp:express.Application = express();
|
||||||
|
|
||||||
|
ConsoleHelper.printInfo("Starting Binato...");
|
||||||
|
|
||||||
if (config["prometheus"]["enabled"]) {
|
if (config["prometheus"]["enabled"]) {
|
||||||
const register:Registry = new Registry();
|
const register:Registry = new Registry();
|
||||||
register.setDefaultLabels({ app: "nodejs_binato" });
|
register.setDefaultLabels({ app: "nodejs_binato" });
|
||||||
|
@ -20,14 +30,14 @@ if (config["prometheus"]["enabled"]) {
|
||||||
res.end(await register.metrics());
|
res.end(await register.metrics());
|
||||||
});
|
});
|
||||||
|
|
||||||
prometheusApp.listen(config["prometheus"]["port"], () => ConsoleHelper.printBancho(`Prometheus metrics listening at port ${config["prometheus"]["port"]}`));
|
prometheusApp.listen(config["prometheus"]["port"], () => ConsoleHelper.printInfo(`Prometheus metrics listening at port ${config["prometheus"]["port"]}`));
|
||||||
} else {
|
} else {
|
||||||
ConsoleHelper.printWarn("Prometheus is disabled!");
|
ConsoleHelper.printWarn("Prometheus is disabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config["express"]["compression"]) {
|
if (config["express"]["compression"]) {
|
||||||
binatoApp.use(compression());
|
binatoApp.use(compression());
|
||||||
ConsoleHelper.printBancho("Compression is enabled");
|
ConsoleHelper.printInfo("Compression is enabled");
|
||||||
} else {
|
} else {
|
||||||
ConsoleHelper.printWarn("Compression is disabled");
|
ConsoleHelper.printWarn("Compression is disabled");
|
||||||
}
|
}
|
||||||
|
@ -58,4 +68,4 @@ binatoApp.use((req, res) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
binatoApp.listen(config.express.port, () => ConsoleHelper.printBancho(`Binato is up! Listening at port ${config.express.port}`));
|
binatoApp.listen(config.express.port, () => ConsoleHelper.printInfo(`Binato is up! Listening at port ${config.express.port}`));
|
|
@ -7,12 +7,14 @@ enum LogType {
|
||||||
};
|
};
|
||||||
|
|
||||||
const LogTags = {
|
const LogTags = {
|
||||||
BANCHO: chalk.bgMagenta(chalk.black(" BCHO ")),
|
INFO: chalk.bgGreen(chalk.black(" INFO ")),
|
||||||
WEBREQ: chalk.bgGreen(chalk.black(" WEBR ")),
|
BANCHO: chalk.bgMagenta(chalk.black(" BANCHO ")),
|
||||||
|
WEBREQ: chalk.bgGreen(chalk.black(" WEBREQ ")),
|
||||||
CHAT: chalk.bgCyan(chalk.black(" CHAT ")),
|
CHAT: chalk.bgCyan(chalk.black(" CHAT ")),
|
||||||
WARN: chalk.bgYellow(chalk.black(" WARN ")),
|
WARN: chalk.bgYellow(chalk.black(" WARN ")),
|
||||||
ERROR: chalk.bgRed(" ERRR "),
|
ERROR: chalk.bgRed(" ERRR "),
|
||||||
REDIS: chalk.bgRed(chalk.white(" RDIS "))
|
REDIS: chalk.bgRed(chalk.white(" bREDIS ")),
|
||||||
|
STREAM: chalk.bgBlue(chalk.black(" STREAM "))
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
function correctValue(i:number) : string {
|
function correctValue(i:number) : string {
|
||||||
|
@ -41,6 +43,14 @@ export class ConsoleHelper {
|
||||||
log(LogTags.WEBREQ, s);
|
log(LogTags.WEBREQ, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static printStream(s:string) : void {
|
||||||
|
log(LogTags.STREAM, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printInfo(s:string) : void {
|
||||||
|
log(LogTags.INFO, s);
|
||||||
|
}
|
||||||
|
|
||||||
public static printBancho(s:string) : void {
|
public static printBancho(s:string) : void {
|
||||||
log(LogTags.BANCHO, s);
|
log(LogTags.BANCHO, s);
|
||||||
}
|
}
|
||||||
|
|
3
Constants.ts
Normal file
3
Constants.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export abstract class Constants {
|
||||||
|
public static readonly DEBUG = true;
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { ConsoleHelper } from "../ConsoleHelper";
|
import { ConsoleHelper } from "../ConsoleHelper";
|
||||||
|
import { ChatManager } from "./ChatManager";
|
||||||
import { Database } from "./objects/Database";
|
import { Database } from "./objects/Database";
|
||||||
import { LatLng } from "./objects/LatLng";
|
import { LatLng } from "./objects/LatLng";
|
||||||
import { LoginProcess } from "./LoginProcess";
|
import { LoginProcess } from "./LoginProcess";
|
||||||
|
@ -9,17 +10,13 @@ import { RedisClientType, createClient } from "redis";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { UserArray } from "./objects/UserArray";
|
import { UserArray } from "./objects/UserArray";
|
||||||
import { User } from "./objects/User";
|
import { User } from "./objects/User";
|
||||||
const config:any = JSON.parse(readFileSync(__dirname + "/config.json").toString());
|
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||||
|
import { MultiplayerManager } from "./MultiplayerManager";
|
||||||
|
const config:any = JSON.parse(readFileSync("./config.json").toString());
|
||||||
// TODO: Port osu-packet to TypeScript
|
// TODO: Port osu-packet to TypeScript
|
||||||
const osu = require("osu-packet");
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
/*const
|
/*Streams = require("./Streams.js");*/
|
||||||
loginHandler = require("./loginHandler.js"),
|
|
||||||
parseUserData = require("./util/parseUserData.js"),
|
|
||||||
getUserFromToken = require("./util/getUserByToken.js"),
|
|
||||||
getUserById = require("./util/getUserById.js"),
|
|
||||||
bakedResponses = require("./bakedResponses.js"),
|
|
||||||
Streams = require("./Streams.js");*/
|
|
||||||
|
|
||||||
const DB:Database = new Database(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
const DB:Database = new Database(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
||||||
// Close any unclosed db matches on startup
|
// Close any unclosed db matches on startup
|
||||||
|
@ -27,11 +24,23 @@ const DB:Database = new Database(config.database.address, config.database.port,
|
||||||
DB.query("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
DB.query("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Users funkyArray for session storage
|
// User session storage
|
||||||
const users = new UserArray();
|
const users:UserArray = new UserArray();
|
||||||
|
|
||||||
|
// DataStream storage
|
||||||
|
const streams:DataStreamArray = new DataStreamArray();
|
||||||
|
|
||||||
|
// ChatManager
|
||||||
|
const chatManager:ChatManager = new ChatManager(streams);
|
||||||
|
chatManager.AddChatChannel("osu", "The main channel");
|
||||||
|
chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff");
|
||||||
|
chatManager.AddChatChannel("english", "Talk in exclusively English");
|
||||||
|
chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese");
|
||||||
|
|
||||||
|
const multiplayerManager:MultiplayerManager = new MultiplayerManager(streams);
|
||||||
|
|
||||||
// Add the bot user
|
// Add the bot user
|
||||||
const botUser:User = users.add("bot", new User(3, "SillyBot", "bot", DB));
|
const botUser:User = users.add("bot", new User(3, "SillyBot", "bot", DB, users, streams, chatManager));
|
||||||
// Set the bot's position on the map
|
// Set the bot's position on the map
|
||||||
botUser.location = new LatLng(50, -32);
|
botUser.location = new LatLng(50, -32);
|
||||||
|
|
||||||
|
@ -84,26 +93,6 @@ setInterval(() => {
|
||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
// Init stream class
|
|
||||||
//Streams.init();
|
|
||||||
|
|
||||||
// An array containing all chat channels
|
|
||||||
/*global.channels = [
|
|
||||||
{ channelName:"#osu", channelTopic:"The main channel", channelUserCount: 0, locked: false },
|
|
||||||
{ channelName:"#userlog", channelTopic:"Log about stuff doing go on yes very", channelUserCount: 0, locked: false },
|
|
||||||
{ channelName:"#lobby", channelTopic:"Talk about multiplayer stuff", channelUserCount: 0, locked: false },
|
|
||||||
{ channelName:"#english", channelTopic:"Talk in exclusively English", channelUserCount: 0, locked: false },
|
|
||||||
{ channelName:"#japanese", channelTopic:"Talk in exclusively Japanese", channelUserCount: 0, locked: false },
|
|
||||||
];*/
|
|
||||||
|
|
||||||
// Create a stream for each chat channel
|
|
||||||
/*for (let i = 0; i < global.channels.length; i++) {
|
|
||||||
Streams.addStream(global.channels[i].channelName, false);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Add a stream for the multiplayer lobby
|
|
||||||
//Streams.addStream("multiplayer_lobby", false);
|
|
||||||
|
|
||||||
// Include packets
|
// Include packets
|
||||||
/*const ChangeAction = require("./Packets/ChangeAction.js"),
|
/*const ChangeAction = require("./Packets/ChangeAction.js"),
|
||||||
SendPublicMessage = require("./Packets/SendPublicMessage.js"),
|
SendPublicMessage = require("./Packets/SendPublicMessage.js"),
|
||||||
|
@ -123,9 +112,11 @@ setInterval(() => {
|
||||||
TourneyMatchSpecialInfo = require("./Packets/TourneyMatchSpecialInfo.js"),
|
TourneyMatchSpecialInfo = require("./Packets/TourneyMatchSpecialInfo.js"),
|
||||||
TourneyMatchJoinChannel = require("./Packets/TourneyMatchSpecialInfo.js"),
|
TourneyMatchJoinChannel = require("./Packets/TourneyMatchSpecialInfo.js"),
|
||||||
TourneyMatchLeaveChannel = require("./Packets/TourneyLeaveMatchChannel.js");*/
|
TourneyMatchLeaveChannel = require("./Packets/TourneyLeaveMatchChannel.js");*/
|
||||||
|
import { ChangeAction } from "./packets/ChangeAction";
|
||||||
// A class for managing everything multiplayer
|
import { Logout } from "./packets/Logout";
|
||||||
//const multiplayerManager:MultiplayerManager = new MultiplayerManager();
|
import { UserPresence } from "./packets/UserPresence";
|
||||||
|
import { UserStatsRequest } from "./packets/UserStatsRequest";
|
||||||
|
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||||
|
|
||||||
const EMPTY_BUFFER = Buffer.alloc(0);
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
||||||
|
|
||||||
|
@ -143,7 +134,7 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
if (DB.connected) {
|
if (DB.connected) {
|
||||||
// Client doesn't have a token yet, let's auth them!
|
// Client doesn't have a token yet, let's auth them!
|
||||||
|
|
||||||
await LoginProcess(req, res, packet, DB, users);
|
await LoginProcess(req, res, packet, DB, users, streams, chatManager);
|
||||||
DB.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [users.getLength() - 1]);
|
DB.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [users.getLength() - 1]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -166,13 +157,13 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
|
|
||||||
// Go through each packet sent by the client
|
// Go through each packet sent by the client
|
||||||
for (let CurrentPacket of PacketData) {
|
for (let CurrentPacket of PacketData) {
|
||||||
/*switch (CurrentPacket.id) {
|
switch (CurrentPacket.id) {
|
||||||
case Packets.Client_ChangeAction:
|
case Packets.Client_ChangeAction:
|
||||||
ChangeAction(PacketUser, CurrentPacket.data);
|
ChangeAction(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SendPublicMessage:
|
case Packets.Client_SendPublicMessage:
|
||||||
SendPublicMessage(PacketUser, CurrentPacket.data);
|
//SendPublicMessage(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_Logout:
|
case Packets.Client_Logout:
|
||||||
|
@ -184,124 +175,124 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_StartSpectating:
|
case Packets.Client_StartSpectating:
|
||||||
Spectator.startSpectatingUser(PacketUser, CurrentPacket.data);
|
//Spectator.startSpectatingUser(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SpectateFrames:
|
case Packets.Client_SpectateFrames:
|
||||||
Spectator.sendSpectatorFrames(PacketUser, CurrentPacket.data);
|
//Spectator.sendSpectatorFrames(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_StopSpectating:
|
case Packets.Client_StopSpectating:
|
||||||
Spectator.stopSpectatingUser(PacketUser);
|
//Spectator.stopSpectatingUser(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SendPrivateMessage:
|
case Packets.Client_SendPrivateMessage:
|
||||||
SendPrivateMessage(PacketUser, CurrentPacket.data);
|
//SendPrivateMessage(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_JoinLobby:
|
case Packets.Client_JoinLobby:
|
||||||
multiplayerManager.userEnterLobby(PacketUser);
|
//multiplayerManager.userEnterLobby(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_PartLobby:
|
case Packets.Client_PartLobby:
|
||||||
multiplayerManager.userLeaveLobby(PacketUser);
|
//multiplayerManager.userLeaveLobby(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_CreateMatch:
|
case Packets.Client_CreateMatch:
|
||||||
await multiplayerManager.createMultiplayerMatch(PacketUser, CurrentPacket.data);
|
//await multiplayerManager.createMultiplayerMatch(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_JoinMatch:
|
case Packets.Client_JoinMatch:
|
||||||
multiplayerManager.joinMultiplayerMatch(PacketUser, CurrentPacket.data);
|
//multiplayerManager.joinMultiplayerMatch(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchChangeSlot:
|
case Packets.Client_MatchChangeSlot:
|
||||||
PacketUser.currentMatch.moveToSlot(PacketUser, CurrentPacket.data);
|
//PacketUser.currentMatch.moveToSlot(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchReady:
|
case Packets.Client_MatchReady:
|
||||||
PacketUser.currentMatch.setStateReady(PacketUser);
|
//PacketUser.currentMatch.setStateReady(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchChangeSettings:
|
case Packets.Client_MatchChangeSettings:
|
||||||
await PacketUser.currentMatch.updateMatch(PacketUser, CurrentPacket.data);
|
//await PacketUser.currentMatch.updateMatch(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchNotReady:
|
case Packets.Client_MatchNotReady:
|
||||||
PacketUser.currentMatch.setStateNotReady(PacketUser);
|
//PacketUser.currentMatch.setStateNotReady(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_PartMatch:
|
case Packets.Client_PartMatch:
|
||||||
await multiplayerManager.leaveMultiplayerMatch(PacketUser);
|
//await multiplayerManager.leaveMultiplayerMatch(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Also handles user kick if the slot has a user
|
// Also handles user kick if the slot has a user
|
||||||
case Packets.Client_MatchLock:
|
case Packets.Client_MatchLock:
|
||||||
PacketUser.currentMatch.lockMatchSlot(PacketUser, CurrentPacket.data);
|
//PacketUser.currentMatch.lockMatchSlot(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchNoBeatmap:
|
case Packets.Client_MatchNoBeatmap:
|
||||||
PacketUser.currentMatch.missingBeatmap(PacketUser);
|
//PacketUser.currentMatch.missingBeatmap(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchSkipRequest:
|
case Packets.Client_MatchSkipRequest:
|
||||||
PacketUser.currentMatch.matchSkip(PacketUser);
|
//PacketUser.currentMatch.matchSkip(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchHasBeatmap:
|
case Packets.Client_MatchHasBeatmap:
|
||||||
PacketUser.currentMatch.notMissingBeatmap(PacketUser);
|
//PacketUser.currentMatch.notMissingBeatmap(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchTransferHost:
|
case Packets.Client_MatchTransferHost:
|
||||||
PacketUser.currentMatch.transferHost(PacketUser, CurrentPacket.data);
|
//PacketUser.currentMatch.transferHost(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchChangeMods:
|
case Packets.Client_MatchChangeMods:
|
||||||
PacketUser.currentMatch.updateMods(PacketUser, CurrentPacket.data);
|
//PacketUser.currentMatch.updateMods(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchStart:
|
case Packets.Client_MatchStart:
|
||||||
PacketUser.currentMatch.startMatch();
|
//PacketUser.currentMatch.startMatch();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchLoadComplete:
|
case Packets.Client_MatchLoadComplete:
|
||||||
PacketUser.currentMatch.matchPlayerLoaded(PacketUser);
|
//PacketUser.currentMatch.matchPlayerLoaded(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchComplete:
|
case Packets.Client_MatchComplete:
|
||||||
await PacketUser.currentMatch.onPlayerFinishMatch(PacketUser);
|
//await PacketUser.currentMatch.onPlayerFinishMatch(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchScoreUpdate:
|
case Packets.Client_MatchScoreUpdate:
|
||||||
PacketUser.currentMatch.updatePlayerScore(PacketUser, CurrentPacket.data);
|
//PacketUser.currentMatch.updatePlayerScore(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchFailed:
|
case Packets.Client_MatchFailed:
|
||||||
PacketUser.currentMatch.matchFailed(PacketUser);
|
//PacketUser.currentMatch.matchFailed(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_MatchChangeTeam:
|
case Packets.Client_MatchChangeTeam:
|
||||||
PacketUser.currentMatch.changeTeam(PacketUser);
|
//PacketUser.currentMatch.changeTeam(PacketUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_ChannelJoin:
|
case Packets.Client_ChannelJoin:
|
||||||
ChannelJoin(PacketUser, CurrentPacket.data);
|
//ChannelJoin(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_ChannelPart:
|
case Packets.Client_ChannelPart:
|
||||||
ChannelPart(PacketUser, CurrentPacket.data);
|
//ChannelPart(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SetAwayMessage:
|
case Packets.Client_SetAwayMessage:
|
||||||
SetAwayMessage(PacketUser, CurrentPacket.data);
|
//SetAwayMessage(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_FriendAdd:
|
case Packets.Client_FriendAdd:
|
||||||
AddFriend(PacketUser, CurrentPacket.data);
|
//AddFriend(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_FriendRemove:
|
case Packets.Client_FriendRemove:
|
||||||
RemoveFriend(PacketUser, CurrentPacket.data);
|
//RemoveFriend(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_UserStatsRequest:
|
case Packets.Client_UserStatsRequest:
|
||||||
|
@ -309,19 +300,19 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SpecialMatchInfoRequest:
|
case Packets.Client_SpecialMatchInfoRequest:
|
||||||
TourneyMatchSpecialInfo(PacketUser, CurrentPacket.data);
|
//TourneyMatchSpecialInfo(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SpecialJoinMatchChannel:
|
case Packets.Client_SpecialJoinMatchChannel:
|
||||||
TourneyMatchJoinChannel(PacketUser, CurrentPacket.data);
|
//TourneyMatchJoinChannel(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_SpecialLeaveMatchChannel:
|
case Packets.Client_SpecialLeaveMatchChannel:
|
||||||
TourneyMatchLeaveChannel(PacketUser, CurrentPacket.data);
|
//TourneyMatchLeaveChannel(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_Invite:
|
case Packets.Client_Invite:
|
||||||
MultiplayerInvite(PacketUser, CurrentPacket.data);
|
//MultiplayerInvite(PacketUser, CurrentPacket.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_UserPresenceRequest:
|
case Packets.Client_UserPresenceRequest:
|
||||||
|
@ -334,7 +325,7 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||||
// Print out unimplemented packet
|
// Print out unimplemented packet
|
||||||
console.dir(CurrentPacket);
|
console.dir(CurrentPacket);
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = PacketUser.queue;
|
responseData = PacketUser.queue;
|
||||||
|
|
19
server/ChatManager.ts
Normal file
19
server/ChatManager.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Channel } from "./objects/Channel";
|
||||||
|
import { ConsoleHelper } from "../ConsoleHelper";
|
||||||
|
import { FunkyArray } from "./objects/FunkyArray";
|
||||||
|
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||||
|
|
||||||
|
export class ChatManager {
|
||||||
|
public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
||||||
|
public streams:DataStreamArray;
|
||||||
|
|
||||||
|
public constructor(streams:DataStreamArray) {
|
||||||
|
this.streams = streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddChatChannel(name:string, description:string, forceJoin:boolean = false) {
|
||||||
|
const stream = this.streams.CreateStream(`chat_channel:${name}`, false);
|
||||||
|
this.chatChannels.add(name, new Channel(name, description, stream, forceJoin));
|
||||||
|
ConsoleHelper.printChat(`Created chat channel [${name}]`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,12 @@ import { readFileSync } from "fs";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { UserArray } from "./objects/UserArray";
|
import { UserArray } from "./objects/UserArray";
|
||||||
import { User } from "./objects/User";
|
import { User } from "./objects/User";
|
||||||
const config:any = JSON.parse(readFileSync(__dirname + "/config.json").toString());
|
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||||
|
import { ChatManager } from "./ChatManager";
|
||||||
|
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||||
|
import { UserPresence } from "./packets/UserPresence";
|
||||||
|
import { StatusUpdate } from "./packets/StatusUpdate";
|
||||||
|
const config:any = JSON.parse(readFileSync("./config.json").toString());
|
||||||
const { decrypt: aesDecrypt } = require("aes256");
|
const { decrypt: aesDecrypt } = require("aes256");
|
||||||
const osu = require("osu-packet");
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
|
@ -88,7 +93,7 @@ function TestLogin(loginInfo:LoginInfo | undefined, database:Database) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function LoginProcess(req:Request, res:Response, packet:Buffer, database:Database, users:UserArray) {
|
export async function LoginProcess(req:Request, res:Response, packet:Buffer, database:Database, users:UserArray, streams:DataStreamArray, chatManager:ChatManager) {
|
||||||
const loginInfo = LoginInfo.From(packet);
|
const loginInfo = LoginInfo.From(packet);
|
||||||
const loginStartTime = Date.now();
|
const loginStartTime = Date.now();
|
||||||
|
|
||||||
|
@ -145,11 +150,11 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, dat
|
||||||
// Make sure user is not already connected, kick off if so.
|
// Make sure user is not already connected, kick off if so.
|
||||||
const connectedUser = users.getByUsername(loginInfo.username);
|
const connectedUser = users.getByUsername(loginInfo.username);
|
||||||
if (connectedUser != null && !isTourneyClient && !connectedUser.isTourneyUser) {
|
if (connectedUser != null && !isTourneyClient && !connectedUser.isTourneyUser) {
|
||||||
Logout(connectedUser, database);
|
Logout(connectedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retreive the newly created user
|
// Retreive the newly created user
|
||||||
const newUser:User = users.add(newClientToken, new User(userDB.id, loginInfo.username, newClientToken, database));
|
const newUser:User = users.add(newClientToken, new User(userDB.id, loginInfo.username, newClientToken, database, users, streams, chatManager));
|
||||||
// Set tourney client flag
|
// Set tourney client flag
|
||||||
newUser.isTourneyUser = isTourneyClient;
|
newUser.isTourneyUser = isTourneyClient;
|
||||||
newUser.location = userLocation;
|
newUser.location = userLocation;
|
||||||
|
@ -173,14 +178,14 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, dat
|
||||||
osuPacketWriter.LoginPermissions(4);
|
osuPacketWriter.LoginPermissions(4);
|
||||||
|
|
||||||
// After sending the user their friends list send them the online users
|
// After sending the user their friends list send them the online users
|
||||||
//UserPresenceBundle(newUser);
|
UserPresenceBundle(newUser);
|
||||||
|
|
||||||
// Set title screen image
|
// Set title screen image
|
||||||
//osuPacketWriter.TitleUpdate("http://puu.sh/jh7t7/20c04029ad.png|https://osu.ppy.sh/news/123912240253");
|
//osuPacketWriter.TitleUpdate("http://puu.sh/jh7t7/20c04029ad.png|https://osu.ppy.sh/news/123912240253");
|
||||||
|
|
||||||
// Add user panel data packets
|
// Add user panel data packets
|
||||||
//UserPresence(newUser, newUser.id);
|
UserPresence(newUser, newUser.id);
|
||||||
//StatusUpdate(newUser, newUser.id);
|
StatusUpdate(newUser, newUser.id);
|
||||||
|
|
||||||
// peppy pls, why
|
// peppy pls, why
|
||||||
osuPacketWriter.ChannelListingComplete();
|
osuPacketWriter.ChannelListingComplete();
|
||||||
|
@ -201,7 +206,7 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, dat
|
||||||
|
|
||||||
// Construct user's friends list
|
// Construct user's friends list
|
||||||
const userFriends = await database.query("SELECT friendsWith FROM friends WHERE user = ?", [newUser.id]);
|
const userFriends = await database.query("SELECT friendsWith FROM friends WHERE user = ?", [newUser.id]);
|
||||||
let friendsArray = [];
|
let friendsArray = new Array<number>;
|
||||||
for (let i = 0; i < userFriends.length; i++) {
|
for (let i = 0; i < userFriends.length; i++) {
|
||||||
friendsArray.push(userFriends[i].friendsWith);
|
friendsArray.push(userFriends[i].friendsWith);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,20 @@
|
||||||
export abstract class MultiplayerManager {
|
import { DataStream } from "./objects/DataStream";
|
||||||
|
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||||
|
import { FunkyArray } from "./objects/FunkyArray";
|
||||||
|
import { Match } from "./objects/Match";
|
||||||
|
import { User } from "./objects/User";
|
||||||
|
|
||||||
|
export class MultiplayerManager {
|
||||||
|
private matches:FunkyArray<Match> = new FunkyArray<Match>();
|
||||||
|
private readonly lobbyStream:DataStream;
|
||||||
|
|
||||||
|
public constructor(streams:DataStreamArray) {
|
||||||
|
this.lobbyStream = streams.CreateStream("multiplayer:lobby", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JoinLobby(user:User) {
|
||||||
|
if (user.currentMatch != null) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { DataStream } from "./DataStream";
|
||||||
|
|
||||||
|
export class Channel {
|
||||||
|
public name:string;
|
||||||
|
public description:string;
|
||||||
|
public userCount:number = 0;
|
||||||
|
private stream:DataStream;
|
||||||
|
private forceJoin:boolean;
|
||||||
|
|
||||||
|
public constructor(name:string, description:string, stream:DataStream, forceJoin:boolean = false) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.stream = stream;
|
||||||
|
this.forceJoin = forceJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendMessage(message:string) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
45
server/objects/DataStream.ts
Normal file
45
server/objects/DataStream.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||||
|
import { Constants } from "../../Constants";
|
||||||
|
import { DataStreamArray } from "./DataStreamArray";
|
||||||
|
import { User } from "./User";
|
||||||
|
import { UserArray } from "./UserArray";
|
||||||
|
|
||||||
|
export class DataStream {
|
||||||
|
private users:UserArray = new UserArray();
|
||||||
|
private readonly name:string;
|
||||||
|
private readonly parent:DataStreamArray;
|
||||||
|
private readonly removeWhenEmpty:boolean;
|
||||||
|
|
||||||
|
public constructor(name:string, parent:DataStreamArray, removeWhenEmpty:boolean) {
|
||||||
|
this.name = name;
|
||||||
|
this.parent = parent;
|
||||||
|
this.removeWhenEmpty = removeWhenEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddUser(user:User) : void {
|
||||||
|
if (!(user.uuid in this.users.getItems())) {
|
||||||
|
this.users.add(user.uuid, user);
|
||||||
|
ConsoleHelper.printStream(`Added user [${user.username}|${user.uuid}] to stream [${this.name}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveUser(user:User) : void {
|
||||||
|
if (user.uuid in this.users.getItems()) {
|
||||||
|
this.users.remove(user.uuid);
|
||||||
|
ConsoleHelper.printStream(`Removed user [${user.username}|${user.uuid}] from stream [${this.name}]`);
|
||||||
|
}
|
||||||
|
if (this.removeWhenEmpty && this.users.getLength() === 0) {
|
||||||
|
this.parent.remove(this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Send(data:Buffer) {
|
||||||
|
for (let user of this.users.getIterableItems()) {
|
||||||
|
user.addActionToQueue(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Constants.DEBUG) {
|
||||||
|
ConsoleHelper.printStream(`Sent [${data.toString()}] to all users in stream [${this.name}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
server/objects/DataStreamArray.ts
Normal file
18
server/objects/DataStreamArray.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||||
|
import { DataStream } from "./DataStream";
|
||||||
|
import { FunkyArray } from "./FunkyArray";
|
||||||
|
import { User } from "./User";
|
||||||
|
|
||||||
|
export class DataStreamArray extends FunkyArray<DataStream> {
|
||||||
|
public CreateStream(name:string, removeWhenEmpty:boolean = true) : DataStream {
|
||||||
|
const dataStream:DataStream = this.add(name, new DataStream(name, this, removeWhenEmpty));
|
||||||
|
ConsoleHelper.printStream(`Created stream [${name}]`);
|
||||||
|
return dataStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveUserFromAllStreams(user:User) {
|
||||||
|
for (let stream of this.getIterableItems()) {
|
||||||
|
stream.RemoveUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ export class Database {
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!this.connected) {
|
if (!this.connected) {
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
ConsoleHelper.printBancho(`Connected to database. Took ${Date.now() - classCreationTime}ms`);
|
ConsoleHelper.printInfo(`Connected to database. Took ${Date.now() - classCreationTime}ms`);
|
||||||
clearInterval(connectionCheckInterval);
|
clearInterval(connectionCheckInterval);
|
||||||
lastQueryFinished = true;
|
lastQueryFinished = true;
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,19 @@ export class FunkyArray<T> {
|
||||||
private itemKeys:Array<string> = Object.keys(this.items);
|
private itemKeys:Array<string> = Object.keys(this.items);
|
||||||
private iterableArray:Array<T> = new Array<T>();
|
private iterableArray:Array<T> = new Array<T>();
|
||||||
|
|
||||||
public add(uuid:string, item:T, regenerate:boolean = true) : T {
|
public add(key:string, item:T, regenerate:boolean = true) : T {
|
||||||
this.items[uuid] = item;
|
this.items[key] = item;
|
||||||
|
|
||||||
if (regenerate) {
|
if (regenerate) {
|
||||||
this.itemKeys = Object.keys(this.items);
|
this.itemKeys = Object.keys(this.items);
|
||||||
this.regenerateIterableArray();
|
this.regenerateIterableArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.items[uuid];
|
return this.items[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(uuid:string, regenerate:boolean = true) {
|
public remove(key:string, regenerate:boolean = true) {
|
||||||
delete this.items[uuid];
|
delete this.items[key];
|
||||||
if (regenerate) {
|
if (regenerate) {
|
||||||
this.itemKeys = Object.keys(this.items);
|
this.itemKeys = Object.keys(this.items);
|
||||||
this.regenerateIterableArray();
|
this.regenerateIterableArray();
|
||||||
|
|
573
server/objects/Match.ts
Normal file
573
server/objects/Match.ts
Normal file
|
@ -0,0 +1,573 @@
|
||||||
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
|
export class Match {
|
||||||
|
/*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 host:number = 0;
|
||||||
|
public playMode:number = 0;
|
||||||
|
public matchScoringType:number = 0;
|
||||||
|
public matchTeamType:number = 0;
|
||||||
|
public specialModes:number = 0;
|
||||||
|
public seed:number = 0;
|
||||||
|
|
||||||
|
constructor(MatchData = {}) {
|
||||||
|
this.matchId = MatchData.matchId;
|
||||||
|
|
||||||
|
this.roundId = 0;
|
||||||
|
|
||||||
|
this.inProgress = MatchData.inProgress;
|
||||||
|
this.matchStartCountdownActive = false;
|
||||||
|
|
||||||
|
this.matchType = MatchData.matchType;
|
||||||
|
|
||||||
|
this.activeMods = MatchData.activeMods;
|
||||||
|
|
||||||
|
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.slots = MatchData.slots;
|
||||||
|
for (let i = 0; i < this.slots.length; i++) {
|
||||||
|
this.slots[i].mods = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.host = MatchData.host;
|
||||||
|
|
||||||
|
this.playMode = MatchData.playMode;
|
||||||
|
|
||||||
|
this.matchScoringType = MatchData.matchScoringType;
|
||||||
|
this.matchTeamType = MatchData.matchTeamType;
|
||||||
|
this.specialModes = MatchData.specialModes;
|
||||||
|
|
||||||
|
this.seed = MatchData.seed;
|
||||||
|
|
||||||
|
this.matchStreamName = `mp_${this.matchId}`;
|
||||||
|
this.matchChatStreamName = `mp_chat_${this.matchId}`;
|
||||||
|
|
||||||
|
this.matchLoadSlots = null;
|
||||||
|
this.matchSkippedSlots = null;
|
||||||
|
|
||||||
|
this.playerScores = null;
|
||||||
|
|
||||||
|
this.multiplayerExtras = null;
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSlotIdByPlayerId(playerId = 0) {
|
||||||
|
const player = getUserById(playerId);
|
||||||
|
|
||||||
|
if (player != null) return player.matchSlotId;
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveMatch(MatchUser = new User) {
|
||||||
|
// Make sure this leave call is valid
|
||||||
|
if (!MatchUser.inMatch) return;
|
||||||
|
|
||||||
|
// Get the user's slot
|
||||||
|
const slot = this.slots[MatchUser.matchSlotId];
|
||||||
|
|
||||||
|
// Set the slot's status to avaliable
|
||||||
|
slot.playerId = -1;
|
||||||
|
slot.status = 1;
|
||||||
|
|
||||||
|
// Remove the leaving user from the match's stream
|
||||||
|
Streams.removeUserFromStream(this.matchStreamName, MatchUser.uuid);
|
||||||
|
Streams.removeUserFromStream(this.matchChatStreamName, MatchUser.uuid);
|
||||||
|
|
||||||
|
// Send this after removing the user from match streams to avoid a leave notification for self
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
// Remove user from the multiplayer channel for the match
|
||||||
|
osuPacketWriter.ChannelRevoked("#multiplayer");
|
||||||
|
|
||||||
|
MatchUser.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateMatch(MatchUser = new User, 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 == '') 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;
|
||||||
|
|
||||||
|
if (gameNameChanged || gameSeedChanged) {
|
||||||
|
const queryData = [];
|
||||||
|
if (gameNameChanged) {
|
||||||
|
queryData.push(MatchData.gameName);
|
||||||
|
}
|
||||||
|
if (gameSeedChanged) {
|
||||||
|
queryData.push(MatchData.seed);
|
||||||
|
}
|
||||||
|
queryData.push(this.matchId);
|
||||||
|
|
||||||
|
await global.DatabaseHelper.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMatchUpdate() {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
osuPacketWriter.MatchUpdate(this.createOsuMatchJSON());
|
||||||
|
|
||||||
|
// Update all users in the match with new match information
|
||||||
|
if (Streams.exists(this.matchStreamName))
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveToSlot(MatchUser = new User, SlotToMoveTo) {
|
||||||
|
const oldSlot = this.slots[MatchUser.matchSlotId];
|
||||||
|
|
||||||
|
// Set the new slot's data to the user's old slot data
|
||||||
|
this.slots[SlotToMoveTo].playerId = MatchUser.id;
|
||||||
|
MatchUser.matchSlotId = SlotToMoveTo;
|
||||||
|
this.slots[SlotToMoveTo].status = 4;
|
||||||
|
|
||||||
|
// Set the old slot's data to open
|
||||||
|
oldSlot.playerId = -1;
|
||||||
|
oldSlot.status = 1;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
|
||||||
|
// Update the match listing in the lobby to reflect this change
|
||||||
|
global.MultiplayerManager.updateMatchListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeTeam(MatchUser = new User) {
|
||||||
|
const slot = this.slots[MatchUser.matchSlotId];
|
||||||
|
slot.team = slot.team == 0 ? 1 : 0;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateReady(MatchUser = new User) {
|
||||||
|
if (!MatchUser.inMatch) return;
|
||||||
|
|
||||||
|
// Set the user's ready state to ready
|
||||||
|
this.slots[MatchUser.matchSlotId].status = 8;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateNotReady(MatchUser = new User) {
|
||||||
|
if (!MatchUser.inMatch) return;
|
||||||
|
|
||||||
|
// Set the user's ready state to not ready
|
||||||
|
this.slots[MatchUser.matchSlotId].status = 4;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
lockMatchSlot(MatchUser = new User, MatchUserToKick) {
|
||||||
|
// Make sure the user attempting to kick / lock is the host of the match
|
||||||
|
if (this.host != MatchUser.id) return;
|
||||||
|
|
||||||
|
// Make sure the user that is attempting to be kicked is not the host
|
||||||
|
if (this.slots[MatchUserToKick].playerId === this.host) return;
|
||||||
|
|
||||||
|
// Get the data of the slot at the index sent by the client
|
||||||
|
const slot = this.slots[MatchUserToKick];
|
||||||
|
|
||||||
|
let isSlotEmpty = true;
|
||||||
|
|
||||||
|
// If the slot is empty lock/unlock instead of kicking
|
||||||
|
if (slot.playerId === -1)
|
||||||
|
slot.status = slot.status === 1 ? 2 : 1;
|
||||||
|
|
||||||
|
// The slot isn't empty, kick the player
|
||||||
|
else {
|
||||||
|
const kickedPlayer = getUserById(slot.playerId);
|
||||||
|
kickedPlayer.matchSlotId = -1;
|
||||||
|
slot.playerId = -1;
|
||||||
|
slot.status = 1;
|
||||||
|
isSlotEmpty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
|
||||||
|
// Update the match listing in the lobby listing to reflect this change
|
||||||
|
global.MultiplayerManager.updateMatchListing();
|
||||||
|
|
||||||
|
if (!isSlotEmpty) {
|
||||||
|
let cachedPlayerToken = getUserById(slot.playerId).uuid;
|
||||||
|
|
||||||
|
if (cachedPlayerToken !== null && cachedPlayerToken !== "") {
|
||||||
|
// Remove the kicked user from the match stream
|
||||||
|
Streams.removeUserFromStream(this.matchStreamName, cachedPlayerToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
missingBeatmap(MatchUser = new User) {
|
||||||
|
// User is missing the beatmap set the status to reflect it
|
||||||
|
this.slots[MatchUser.matchSlotId].status = 16;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
notMissingBeatmap(MatchUser = new User) {
|
||||||
|
// The user is not missing the beatmap, set the status to normal
|
||||||
|
this.slots[MatchUser.matchSlotId].status = 4;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
matchSkip(MatchUser = new User) {
|
||||||
|
if (this.matchSkippedSlots == null) {
|
||||||
|
this.matchSkippedSlots = [];
|
||||||
|
|
||||||
|
const skippedSlots = this.matchSkippedSlots;
|
||||||
|
|
||||||
|
for (let slot of this.slots) {
|
||||||
|
// Make sure the slot has a user in it
|
||||||
|
if (slot.playerId === -1 || slot.status === 1 || slot.status === 2) continue;
|
||||||
|
|
||||||
|
// Add the slot's user to the loaded checking array
|
||||||
|
skippedSlots.push({playerId: slot.playerId, skipped: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let allSkipped = true;
|
||||||
|
for (let skippedSlot of this.matchSkippedSlots) {
|
||||||
|
// If loadslot belongs to this user then set loaded to true
|
||||||
|
if (skippedSlot.playerId == MatchUser.id) {
|
||||||
|
skippedSlot.skipped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skippedSlot.skipped) continue;
|
||||||
|
|
||||||
|
// A user hasn't skipped
|
||||||
|
allSkipped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All players have finished playing, finish the match
|
||||||
|
if (allSkipped) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
osuPacketWriter.MatchPlayerSkipped(MatchUser.id);
|
||||||
|
osuPacketWriter.MatchSkip();
|
||||||
|
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
|
||||||
|
this.matchSkippedSlots = null;
|
||||||
|
} else {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
osuPacketWriter.MatchPlayerSkipped(MatchUser.id);
|
||||||
|
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transferHost(MatchUser = new User, SlotIDToTransferTo) {
|
||||||
|
// Set the lobby's host to the new user
|
||||||
|
this.host = this.slots[SlotIDToTransferTo].playerId;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fix not being able to add DT when freemod is active
|
||||||
|
updateMods(MatchUser = new User, MatchMods) {
|
||||||
|
// Check if freemod is enabled
|
||||||
|
if (this.specialModes === 1) {
|
||||||
|
this.slots[MatchUser.matchSlotId].mods = MatchMods;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
} else {
|
||||||
|
// Make sure the person updating mods is the host of the match
|
||||||
|
if (this.host !== MatchUser.id) return;
|
||||||
|
|
||||||
|
// Change the matches mods to these new mods
|
||||||
|
// TODO: Do this per user if freemod is enabled
|
||||||
|
this.activeMods = MatchMods;
|
||||||
|
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update match listing in the lobby to reflect this change
|
||||||
|
global.MultiplayerManager.updateMatchListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
startMatch() {
|
||||||
|
// Make sure the match is not already in progress
|
||||||
|
// The client sometimes double fires the start packet
|
||||||
|
if (this.inProgress) return;
|
||||||
|
this.inProgress = true;
|
||||||
|
// Create array for monitoring users until they are ready to play
|
||||||
|
this.matchLoadSlots = [];
|
||||||
|
// Loop through all slots in the match
|
||||||
|
for (let slot of this.slots) {
|
||||||
|
// Make sure the slot has a user in it
|
||||||
|
if (slot.playerId === -1 || slot.status === 1 || slot.status === 2) continue;
|
||||||
|
|
||||||
|
// Add the slot's user to the loaded checking array
|
||||||
|
this.matchLoadSlots.push({
|
||||||
|
playerId: slot.playerId,
|
||||||
|
loaded: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the user's status to playing
|
||||||
|
slot.status = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
osuPacketWriter.MatchStart(this.createOsuMatchJSON());
|
||||||
|
|
||||||
|
// Inform all users in the match that it has started
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
|
||||||
|
// Update all users in the match with new info
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
|
||||||
|
// Update match listing in lobby to show the game is in progress
|
||||||
|
global.MultiplayerManager.updateMatchListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
matchPlayerLoaded(MatchUser = new User) {
|
||||||
|
// Loop through all user load check items and check if all users are loaded
|
||||||
|
let allLoaded = true;
|
||||||
|
for (let loadedSlot of this.matchLoadSlots) {
|
||||||
|
// If loadslot belongs to this user then set loaded to true
|
||||||
|
if (loadedSlot.playerId == MatchUser.id) {
|
||||||
|
loadedSlot.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedSlot.loaded) continue;
|
||||||
|
|
||||||
|
allLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All players have loaded the beatmap, start playing.
|
||||||
|
if (allLoaded) {
|
||||||
|
let osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
osuPacketWriter.MatchAllPlayersLoaded();
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
|
||||||
|
// Blank out user loading array
|
||||||
|
this.matchLoadSlots = null;
|
||||||
|
|
||||||
|
this.playerScores = [];
|
||||||
|
for (let i = 0; i < this.slots.length; i++) {
|
||||||
|
const slot = this.slots[i];
|
||||||
|
if (slot.playerId === -1 || slot.status === 1 || slot.status === 2) continue;
|
||||||
|
|
||||||
|
this.playerScores.push({playerId: slot.playerId, slotId: i, score: 0, isCurrentlyFailed: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPlayerFinishMatch(MatchUser = new User) {
|
||||||
|
if (this.matchLoadSlots == null) {
|
||||||
|
// Repopulate user loading slots again
|
||||||
|
this.matchLoadSlots = [];
|
||||||
|
for (let slot of this.slots) {
|
||||||
|
// Make sure the slot has a user
|
||||||
|
if (slot.playerId === -1 || slot.status === 1 || slot.status === 2) continue;
|
||||||
|
|
||||||
|
// Populate user loading slots with this user's id and load status
|
||||||
|
this.matchLoadSlots.push({
|
||||||
|
playerId: slot.playerId,
|
||||||
|
loaded: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let allLoaded = true;
|
||||||
|
|
||||||
|
// Loop through all loaded slots to make sure all users have finished playing
|
||||||
|
for (let loadedSlot of this.matchLoadSlots) {
|
||||||
|
if (loadedSlot.playerId == MatchUser.id) {
|
||||||
|
loadedSlot.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedSlot.loaded) continue;
|
||||||
|
|
||||||
|
// A user hasn't finished playing
|
||||||
|
allLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All players have finished playing, finish the match
|
||||||
|
if (allLoaded) await this.finishMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
async finishMatch() {
|
||||||
|
if (!this.inProgress) return;
|
||||||
|
this.matchLoadSlots = null;
|
||||||
|
this.inProgress = false;
|
||||||
|
let osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
let queryData = [this.matchId, this.roundId++, this.playMode, this.matchType, this.matchScoringType, this.matchTeamType, this.activeMods, this.beatmapChecksum, (this.specialModes === 1) ? 1 : 0];
|
||||||
|
|
||||||
|
// Loop through all slots in the match
|
||||||
|
for (let slot of this.slots) {
|
||||||
|
// Make sure the slot has a user
|
||||||
|
if (slot.playerId === -1 || slot.status === 1 || slot.status === 2) {
|
||||||
|
queryData.push(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let score = null;
|
||||||
|
for (let _playerScore of this.playerScores) {
|
||||||
|
if (_playerScore.playerId === slot.playerId) {
|
||||||
|
score = _playerScore._raw;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queryData.push(`${slot.playerId}|${score.totalScore}|${score.maxCombo}|${score.count300}|${score.count100}|${score.count50}|${score.countGeki}|${score.countKatu}|${score.countMiss}|${(score.currentHp == 254) ? 1 : 0}${(this.specialModes === 1) ? `|${slot.mods}` : ""}|${score.usingScoreV2 ? 1 : 0}${score.usingScoreV2 ? `|${score.comboPortion}|${score.bonusPortion}` : ""}`);
|
||||||
|
|
||||||
|
// Set the user's status back to normal from playing
|
||||||
|
slot.status = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(queryData);
|
||||||
|
|
||||||
|
osuPacketWriter.MatchComplete();
|
||||||
|
|
||||||
|
// Inform all users in the match that it is complete
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
|
||||||
|
// Update all users in the match with new info
|
||||||
|
this.sendMatchUpdate();
|
||||||
|
|
||||||
|
// Update match info in the lobby to reflect that the match has finished
|
||||||
|
global.MultiplayerManager.updateMatchListing();
|
||||||
|
|
||||||
|
if (this.multiplayerExtras != null) this.multiplayerExtras.onMatchFinished(JSON.parse(JSON.stringify(this.playerScores)));
|
||||||
|
|
||||||
|
await global.DatabaseHelper.query("INSERT INTO mp_match_rounds (id, match_id, round_id, round_mode, match_type, round_scoring_type, round_team_type, round_mods, beatmap_md5, freemod, player0, player1, player2, player3, player4, player5, player6, player7, player8, player9, player10, player11, player12, player13, player14, player15) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", queryData);
|
||||||
|
|
||||||
|
this.playerScores = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePlayerScore(MatchPlayer = new User, MatchScoreData) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
// Make sure the user's slot ID is not invalid
|
||||||
|
if (this.matchSlotId == -1) return;
|
||||||
|
|
||||||
|
// Get the user's current slotID and append it to the givien data, just incase.
|
||||||
|
MatchScoreData.id = MatchPlayer.matchSlotId;
|
||||||
|
|
||||||
|
// Update the playerScores array accordingly
|
||||||
|
for (let playerScore of this.playerScores) {
|
||||||
|
if (playerScore.playerId == MatchPlayer.id) {
|
||||||
|
playerScore.score = MatchScoreData.totalScore;
|
||||||
|
playerScore.isCurrentlyFailed = MatchScoreData.currentHp == 254;
|
||||||
|
playerScore._raw = MatchScoreData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osuPacketWriter.MatchScoreUpdate(MatchScoreData);
|
||||||
|
|
||||||
|
// Send the newly updated score to all users in the match
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
matchFailed(MatchUser = new User) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
// Make sure the user's slot ID is not invalid
|
||||||
|
if (MatchUser.matchSlotId == -1) return;
|
||||||
|
|
||||||
|
osuPacketWriter.MatchPlayerFailed(MatchUser.id);
|
||||||
|
|
||||||
|
Streams.sendToStream(this.matchStreamName, osuPacketWriter.toBuffer, null);
|
||||||
|
}*/
|
||||||
|
}
|
3
server/objects/Slot.ts
Normal file
3
server/objects/Slot.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export class Slot {
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
import { Database } from "./Database";
|
import { Database } from "./Database";
|
||||||
import { LatLng } from "./LatLng";
|
import { LatLng } from "./LatLng";
|
||||||
import { RankingModes } from "../enums/RankingModes";
|
import { RankingModes } from "../enums/RankingModes";
|
||||||
|
import { Match } from "./Match";
|
||||||
|
import { DataStream } from "./DataStream";
|
||||||
|
import { UserArray } from "./UserArray";
|
||||||
|
import { DataStreamArray } from "./DataStreamArray";
|
||||||
|
import { ChatManager } from "../ChatManager";
|
||||||
//const StatusUpdate = require("./Packets/StatusUpdate.js");
|
//const StatusUpdate = require("./Packets/StatusUpdate.js");
|
||||||
|
|
||||||
const rankingModes = [
|
const rankingModes = [
|
||||||
|
@ -12,6 +17,10 @@ const rankingModes = [
|
||||||
export class User {
|
export class User {
|
||||||
private static readonly EMPTY_BUFFER = Buffer.alloc(0);
|
private static readonly EMPTY_BUFFER = Buffer.alloc(0);
|
||||||
|
|
||||||
|
public users:UserArray;
|
||||||
|
public streams:DataStreamArray;
|
||||||
|
public chatManager:ChatManager;
|
||||||
|
|
||||||
public id:number;
|
public id:number;
|
||||||
public username:string;
|
public username:string;
|
||||||
public uuid:string;
|
public uuid:string;
|
||||||
|
@ -21,6 +30,7 @@ export class User {
|
||||||
|
|
||||||
// Binato data
|
// Binato data
|
||||||
public rankingMode:RankingModes = RankingModes.PP;
|
public rankingMode:RankingModes = RankingModes.PP;
|
||||||
|
public spectatorStream:DataStream | null = null;
|
||||||
|
|
||||||
// osu! data
|
// osu! data
|
||||||
public playMode:number = 0;
|
public playMode:number = 0;
|
||||||
|
@ -47,7 +57,7 @@ export class User {
|
||||||
public pp:number = 0;
|
public pp:number = 0;
|
||||||
|
|
||||||
// Multiplayer data
|
// Multiplayer data
|
||||||
public currentMatch = null;
|
public currentMatch:Match | null = null;
|
||||||
public matchSlotId:number = -1;
|
public matchSlotId:number = -1;
|
||||||
public inMatch:boolean = false;
|
public inMatch:boolean = false;
|
||||||
|
|
||||||
|
@ -56,12 +66,16 @@ export class User {
|
||||||
|
|
||||||
public dbConnection:Database;
|
public dbConnection:Database;
|
||||||
|
|
||||||
public constructor(id:number, username:string, uuid:string, dbConnection:Database) {
|
public constructor(id:number, username:string, uuid:string, dbConnection:Database, users:UserArray, streams:DataStreamArray, chatManager:ChatManager) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
|
||||||
this.dbConnection = dbConnection;
|
this.dbConnection = dbConnection;
|
||||||
|
|
||||||
|
this.users = users;
|
||||||
|
this.streams = streams;
|
||||||
|
this.chatManager = chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concats new actions to the user's queue
|
// Concats new actions to the user's queue
|
||||||
|
|
11
server/packets/ChangeAction.ts
Normal file
11
server/packets/ChangeAction.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { User } from "../objects/User";
|
||||||
|
import { StatusUpdate } from "./StatusUpdate";
|
||||||
|
|
||||||
|
export function ChangeAction(user:User, data:any) {
|
||||||
|
user.updatePresence(data);
|
||||||
|
|
||||||
|
if (user.spectatorStream != null) {
|
||||||
|
const statusUpdate = StatusUpdate(user, user.id, false);
|
||||||
|
user.spectatorStream.Send(statusUpdate);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,19 @@
|
||||||
import { ConsoleHelper } from "../../ConsoleHelper";
|
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||||
import { Database } from "../objects/Database";
|
import { Database } from "../objects/Database";
|
||||||
|
import { DataStreamArray } from "../objects/DataStreamArray";
|
||||||
import { User } from "../objects/User";
|
import { User } from "../objects/User";
|
||||||
|
|
||||||
export async function Logout(user:User, database:Database) {
|
export async function Logout(user:User) {
|
||||||
|
if (user.uuid === "bot") throw "Tried to log bot out, WTF???";
|
||||||
|
|
||||||
|
const logoutStartTime = Date.now();
|
||||||
|
|
||||||
|
user.streams.RemoveUserFromAllStreams(user);
|
||||||
|
|
||||||
|
// Remove user from user list
|
||||||
|
user.users.remove(user.uuid);
|
||||||
|
|
||||||
|
await user.dbConnection.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [user.users.getLength() - 1]);
|
||||||
|
|
||||||
|
ConsoleHelper.printBancho(`User logged out, took ${Date.now() - logoutStartTime}ms. [User: ${user.username}]`);
|
||||||
}
|
}
|
37
server/packets/StatusUpdate.ts
Normal file
37
server/packets/StatusUpdate.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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) {
|
||||||
|
if (id == 3) return; // Ignore Bot
|
||||||
|
|
||||||
|
// Create new osu packet writer
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
// Get user's class
|
||||||
|
const userData = user.users.getById(id);
|
||||||
|
|
||||||
|
if (userData == null) return;
|
||||||
|
|
||||||
|
let UserStatusObject = {
|
||||||
|
userId: userData.id,
|
||||||
|
status: userData.actionID,
|
||||||
|
statusText: userData.actionText,
|
||||||
|
beatmapChecksum: userData.beatmapChecksum,
|
||||||
|
currentMods: userData.currentMods,
|
||||||
|
playMode: userData.playMode,
|
||||||
|
beatmapId: userData.beatmapID,
|
||||||
|
rankedScore: userData.rankedScore,
|
||||||
|
accuracy: userData.accuracy * 0.01, // Scale from 0:100 to 0:1
|
||||||
|
playCount: userData.playCount,
|
||||||
|
totalScore: userData.totalScore,
|
||||||
|
rank: userData.rank,
|
||||||
|
performance: (userData.rankingMode == RankingModes.PP ? userData.pp : 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
osuPacketWriter.HandleOsuUpdate(UserStatusObject);
|
||||||
|
|
||||||
|
// Send data to user's queue
|
||||||
|
if (sendImmidiate) user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
else return osuPacketWriter.toBuffer;
|
||||||
|
}
|
24
server/packets/UserPresence.ts
Normal file
24
server/packets/UserPresence.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { User } from "../objects/User";
|
||||||
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
|
export function UserPresence(user:User, id:number, sendImmidiate:boolean = true) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
const userData = user.users.getById(id);
|
||||||
|
|
||||||
|
if (userData == null) return;
|
||||||
|
|
||||||
|
osuPacketWriter.UserPresence({
|
||||||
|
userId: id,
|
||||||
|
username: userData.username,
|
||||||
|
timezone: 0,
|
||||||
|
countryId: userData.countryID,
|
||||||
|
permissions: 4,
|
||||||
|
longitude: userData.location.longitude,
|
||||||
|
latitude: userData.location.latitude,
|
||||||
|
rank: userData.rank
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sendImmidiate) userData.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
else return osuPacketWriter.toBuffer;
|
||||||
|
}
|
17
server/packets/UserPresenceBundle.ts
Normal file
17
server/packets/UserPresenceBundle.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { User } from "../objects/User";
|
||||||
|
const osu = require("osu-packet");
|
||||||
|
|
||||||
|
export function UserPresenceBundle(user:User, sendImmidiate:boolean = true) {
|
||||||
|
const osuPacketWriter = new osu.Bancho.Writer;
|
||||||
|
|
||||||
|
let userIds:Array<number> = new Array<number>();
|
||||||
|
|
||||||
|
for (let userData of user.users.getIterableItems()) {
|
||||||
|
userIds.push(userData.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
osuPacketWriter.UserPresenceBundle(userIds);
|
||||||
|
|
||||||
|
if (sendImmidiate) user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||||
|
else return osuPacketWriter.toBuffer;
|
||||||
|
}
|
13
server/packets/UserStatsRequest.ts
Normal file
13
server/packets/UserStatsRequest.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { User } from "../objects/User";
|
||||||
|
import { StatusUpdate } from "./StatusUpdate";
|
||||||
|
import { UserPresence } from "./UserPresence";
|
||||||
|
import { UserPresenceBundle } from "./UserPresenceBundle";
|
||||||
|
|
||||||
|
export function UserStatsRequest(user:User, data:Array<number>) {
|
||||||
|
UserPresenceBundle(user);
|
||||||
|
|
||||||
|
for (let id of data) {
|
||||||
|
UserPresence(user, id);
|
||||||
|
StatusUpdate(user, id);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue