Make chat work
This commit is contained in:
parent
a09543b2fb
commit
3da964f5d6
8 changed files with 195 additions and 58 deletions
|
@ -1,4 +1,5 @@
|
|||
import { ConsoleHelper } from "../ConsoleHelper";
|
||||
import { Channel } from "./objects/Channel";
|
||||
import { ChatManager } from "./ChatManager";
|
||||
import { Database } from "./objects/Database";
|
||||
import { LatLng } from "./objects/LatLng";
|
||||
|
@ -16,8 +17,6 @@ const config:any = JSON.parse(readFileSync("./config.json").toString());
|
|||
// TODO: Port osu-packet to TypeScript
|
||||
const osu = require("osu-packet");
|
||||
|
||||
/*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 () => {
|
||||
// Close any unclosed db matches on startup
|
||||
DB.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||
|
@ -32,7 +31,7 @@ const streams:DataStreamArray = new DataStreamArray();
|
|||
|
||||
// ChatManager
|
||||
const chatManager:ChatManager = new ChatManager(streams);
|
||||
chatManager.AddChatChannel("osu", "The main channel");
|
||||
chatManager.AddChatChannel("osu", "The main channel", true);
|
||||
chatManager.AddChatChannel("lobby", "Talk about multiplayer stuff");
|
||||
chatManager.AddChatChannel("english", "Talk in exclusively English");
|
||||
chatManager.AddChatChannel("japanese", "Talk in exclusively Japanese");
|
||||
|
@ -82,42 +81,25 @@ if (config.redis.enabled) {
|
|||
})();
|
||||
} else ConsoleHelper.printWarn("Redis is disabled!");
|
||||
|
||||
// Import packets
|
||||
import { ChangeAction } from "./packets/ChangeAction";
|
||||
import { Logout } from "./packets/Logout";
|
||||
import { UserPresence } from "./packets/UserPresence";
|
||||
import { UserStatsRequest } from "./packets/UserStatsRequest";
|
||||
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||
|
||||
// User timeout interval
|
||||
setInterval(() => {
|
||||
for (let User of users.getIterableItems()) {
|
||||
if (User.uuid == "bot") continue; // Ignore the bot
|
||||
|
||||
// Logout this user, they're clearly gone.
|
||||
// if (Date.now() >= User.timeoutTime)
|
||||
// Logout(User);
|
||||
if (Date.now() >= User.timeoutTime) {
|
||||
Logout(User);
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
// Include packets
|
||||
/*const ChangeAction = require("./Packets/ChangeAction.js"),
|
||||
SendPublicMessage = require("./Packets/SendPublicMessage.js"),
|
||||
Logout = require("./Packets/Logout.js"),
|
||||
Spectator = require("./Spectator.js"),
|
||||
SendPrivateMessage = require("./Packets/SendPrivateMessage.js"),
|
||||
MultiplayerManager = require("./MultiplayerManager.js"),
|
||||
SetAwayMessage = require("./Packets/SetAwayMessage.js"),
|
||||
ChannelJoin = require("./Packets/ChannelJoin.js"),
|
||||
ChannelPart = require("./Packets/ChannelPart.js"),
|
||||
AddFriend = require("./Packets/AddFriend.js"),
|
||||
RemoveFriend = require("./Packets/RemoveFriend.js"),
|
||||
UserPresenceBundle = require("./Packets/UserPresenceBundle.js"),
|
||||
UserPresence = require("./Packets/UserPresence.js"),
|
||||
UserStatsRequest = require("./Packets/UserStatsRequest.js"),
|
||||
MultiplayerInvite = require("./Packets/MultiplayerInvite.js"),
|
||||
TourneyMatchSpecialInfo = require("./Packets/TourneyMatchSpecialInfo.js"),
|
||||
TourneyMatchJoinChannel = require("./Packets/TourneyMatchSpecialInfo.js"),
|
||||
TourneyMatchLeaveChannel = require("./Packets/TourneyLeaveMatchChannel.js");*/
|
||||
import { ChangeAction } from "./packets/ChangeAction";
|
||||
import { Logout } from "./packets/Logout";
|
||||
import { UserPresence } from "./packets/UserPresence";
|
||||
import { UserStatsRequest } from "./packets/UserStatsRequest";
|
||||
import { UserPresenceBundle } from "./packets/UserPresenceBundle";
|
||||
|
||||
const EMPTY_BUFFER = Buffer.alloc(0);
|
||||
|
||||
export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||
|
@ -163,7 +145,10 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
|||
break;
|
||||
|
||||
case Packets.Client_SendPublicMessage:
|
||||
//SendPublicMessage(PacketUser, CurrentPacket.data);
|
||||
let channel = chatManager.GetChannelByName(CurrentPacket.data.target);
|
||||
if (channel instanceof Channel) {
|
||||
channel.SendMessage(PacketUser, CurrentPacket.data.message);
|
||||
}
|
||||
break;
|
||||
|
||||
case Packets.Client_Logout:
|
||||
|
|
|
@ -2,9 +2,12 @@ import { Channel } from "./objects/Channel";
|
|||
import { ConsoleHelper } from "../ConsoleHelper";
|
||||
import { FunkyArray } from "./objects/FunkyArray";
|
||||
import { DataStreamArray } from "./objects/DataStreamArray";
|
||||
import { User } from "./objects/User";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export class ChatManager {
|
||||
public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
||||
public forceJoinChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
|
||||
public streams:DataStreamArray;
|
||||
|
||||
public constructor(streams:DataStreamArray) {
|
||||
|
@ -13,7 +16,48 @@ export class ChatManager {
|
|||
|
||||
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));
|
||||
const channel = new Channel(`#${name}`, description, stream);
|
||||
this.chatChannels.add(channel.name, channel);
|
||||
if (forceJoin) {
|
||||
this.forceJoinChannels.add(name, channel);
|
||||
}
|
||||
ConsoleHelper.printChat(`Created chat channel [${name}]`);
|
||||
}
|
||||
|
||||
public RemoveChatChannel(channel:Channel | string) {
|
||||
if (channel instanceof Channel) {
|
||||
channel.stream.Delete();
|
||||
this.chatChannels.remove(channel.stream.name);
|
||||
this.forceJoinChannels.remove(channel.stream.name)
|
||||
} else {
|
||||
const chatChannel = this.GetChannelByName(channel);
|
||||
if (chatChannel instanceof Channel) {
|
||||
chatChannel.stream.Delete();
|
||||
this.chatChannels.remove(chatChannel.stream.name);
|
||||
this.forceJoinChannels.remove(chatChannel.stream.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GetChannelByName(channelName:string) : Channel | undefined {
|
||||
return this.chatChannels.getByKey(channelName);
|
||||
}
|
||||
|
||||
public ForceJoinChannels(user:User) {
|
||||
for (let channel of this.forceJoinChannels.getIterableItems()) {
|
||||
channel.Join(user);
|
||||
}
|
||||
}
|
||||
|
||||
public SendChannelListing(user:User) {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
for (let channel of this.chatChannels.getIterableItems()) {
|
||||
osuPacketWriter.ChannelAvailable({
|
||||
channelName: channel.name,
|
||||
channelTopic: channel.description,
|
||||
channelUserCount: channel.userCount
|
||||
});
|
||||
}
|
||||
user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}
|
||||
}
|
|
@ -192,23 +192,13 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, dat
|
|||
// peppy pls, why
|
||||
osuPacketWriter.ChannelListingComplete();
|
||||
|
||||
// Add user to #osu
|
||||
osuPacketWriter.ChannelJoinSuccess("#osu");
|
||||
//if (!Streams.isUserInStream("#osu", newUser.uuid))
|
||||
// Streams.addUserToStream("#osu", newUser.uuid);
|
||||
|
||||
// List all channels out to the client
|
||||
/*for (let i = 0; i < global.channels.length; i++) {
|
||||
osuPacketWriter.ChannelAvailable({
|
||||
channelName: global.channels[i].channelName,
|
||||
channelTopic: global.channels[i].channelTopic,
|
||||
channelUserCount: global.channels[i].channelUserCount
|
||||
});
|
||||
}*/
|
||||
// Setup chat
|
||||
chatManager.ForceJoinChannels(newUser);
|
||||
chatManager.SendChannelListing(newUser);
|
||||
|
||||
// Construct user's friends list
|
||||
const userFriends = await database.query("SELECT friendsWith FROM friends WHERE user = ?", [newUser.id]);
|
||||
let friendsArray = new Array<number>;
|
||||
const friendsArray:Array<number> = new Array<number>();
|
||||
for (let i = 0; i < userFriends.length; i++) {
|
||||
friendsArray.push(userFriends[i].friendsWith);
|
||||
}
|
||||
|
|
|
@ -23,4 +23,18 @@ export function generateSession() : Promise<string> {
|
|||
|
||||
export function generateSessionSync() : string {
|
||||
return randomBytes(12).toString("hex");
|
||||
}
|
||||
|
||||
export function hexlify(data:Buffer) : string {
|
||||
let out:string = "";
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const hex = data[i].toString(16);
|
||||
if (hex.length === 1) {
|
||||
out += `0${hex.toUpperCase()},`;
|
||||
} else {
|
||||
out += `${hex.toUpperCase()},`;
|
||||
}
|
||||
}
|
||||
|
||||
return out.slice(0, out.length - 1);
|
||||
}
|
|
@ -1,20 +1,70 @@
|
|||
import { DataStream } from "./DataStream";
|
||||
import { User } from "./User";
|
||||
const osu = require("osu-packet");
|
||||
|
||||
export class Channel {
|
||||
public name:string;
|
||||
public description:string;
|
||||
public userCount:number = 0;
|
||||
private stream:DataStream;
|
||||
private forceJoin:boolean;
|
||||
public stream:DataStream;
|
||||
private isLocked:boolean = false;
|
||||
|
||||
public constructor(name:string, description:string, stream:DataStream, forceJoin:boolean = false) {
|
||||
public constructor(name:string, description:string, stream:DataStream) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.stream = stream;
|
||||
this.forceJoin = forceJoin;
|
||||
}
|
||||
|
||||
public SendMessage(message:string) {
|
||||
public get userCount() {
|
||||
return this.stream.userCount;
|
||||
}
|
||||
|
||||
public SendMessage(sender:User, message:string) {
|
||||
const isBotCommand = message[0] === "!";
|
||||
|
||||
if (this.isLocked && !isBotCommand) {
|
||||
return this.SendSystemMessage("This channel is currently locked", sender);
|
||||
}
|
||||
|
||||
if (isBotCommand) {
|
||||
if (message.split(" ")[0] === "!lock") {
|
||||
this.isLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
osuPacketWriter.SendMessage({
|
||||
sendingClient: sender.username,
|
||||
message: message,
|
||||
target: this.name,
|
||||
senderId: sender.id
|
||||
});
|
||||
this.stream.SendWithExclusion(osuPacketWriter.toBuffer, sender);
|
||||
}
|
||||
|
||||
public SendSystemMessage(message:string, sendTo?:User) {
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
osuPacketWriter.SendMessage({
|
||||
sendingClient: "System",
|
||||
message: message,
|
||||
target: this.name,
|
||||
senderId: 1
|
||||
});
|
||||
|
||||
if (sendTo instanceof User) {
|
||||
sendTo.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
} else {
|
||||
this.stream.Send(osuPacketWriter.toBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public Join(user:User) {
|
||||
this.stream.AddUser(user);
|
||||
const osuPacketWriter = new osu.Bancho.Writer;
|
||||
osuPacketWriter.ChannelJoinSuccess(this.name);
|
||||
user.addActionToQueue(osuPacketWriter.toBuffer);
|
||||
}
|
||||
|
||||
public Leave(user:User) {
|
||||
this.stream.RemoveUser(user);
|
||||
}
|
||||
}
|
|
@ -3,12 +3,14 @@ import { Constants } from "../../Constants";
|
|||
import { DataStreamArray } from "./DataStreamArray";
|
||||
import { User } from "./User";
|
||||
import { UserArray } from "./UserArray";
|
||||
import { hexlify } from "../Util";
|
||||
|
||||
export class DataStream {
|
||||
private users:UserArray = new UserArray();
|
||||
private readonly name:string;
|
||||
public readonly name:string;
|
||||
private readonly parent:DataStreamArray;
|
||||
private readonly removeWhenEmpty:boolean;
|
||||
private inactive:boolean = false;
|
||||
|
||||
public constructor(name:string, parent:DataStreamArray, removeWhenEmpty:boolean) {
|
||||
this.name = name;
|
||||
|
@ -16,30 +18,66 @@ export class DataStream {
|
|||
this.removeWhenEmpty = removeWhenEmpty;
|
||||
}
|
||||
|
||||
private checkInactive() {
|
||||
if (this.inactive) {
|
||||
throw `Stream ${this.name} is inactive (deleted) and cannot be used here.`;
|
||||
}
|
||||
}
|
||||
|
||||
public get userCount() : number {
|
||||
return this.users.getLength();
|
||||
}
|
||||
|
||||
public AddUser(user:User) : void {
|
||||
this.checkInactive();
|
||||
|
||||
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}]`);
|
||||
ConsoleHelper.printStream(`Added [${user.username}] to stream [${this.name}]`);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoveUser(user:User) : void {
|
||||
this.checkInactive();
|
||||
|
||||
if (user.uuid in this.users.getItems()) {
|
||||
this.users.remove(user.uuid);
|
||||
ConsoleHelper.printStream(`Removed user [${user.username}|${user.uuid}] from stream [${this.name}]`);
|
||||
ConsoleHelper.printStream(`Removed [${user.username}] from stream [${this.name}]`);
|
||||
}
|
||||
if (this.removeWhenEmpty && this.users.getLength() === 0) {
|
||||
this.parent.remove(this.name);
|
||||
this.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
public Delete() {
|
||||
this.parent.DeleteStream(this);
|
||||
}
|
||||
|
||||
public Deactivate() {
|
||||
this.inactive = true;
|
||||
}
|
||||
|
||||
public Send(data:Buffer) {
|
||||
this.checkInactive();
|
||||
|
||||
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}]`);
|
||||
}
|
||||
}
|
||||
|
||||
public SendWithExclusion(data:Buffer, exclude:User) {
|
||||
this.checkInactive();
|
||||
|
||||
for (let user of this.users.getIterableItems()) {
|
||||
if (user.uuid !== exclude.uuid) {
|
||||
user.addActionToQueue(data);
|
||||
}
|
||||
}
|
||||
if (Constants.DEBUG) {
|
||||
ConsoleHelper.printStream(`Sent Buffer<${hexlify(data)}> to all users in stream [${this.name}] excluding user [${exclude.username}]`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,21 @@ export class DataStreamArray extends FunkyArray<DataStream> {
|
|||
return dataStream;
|
||||
}
|
||||
|
||||
public DeleteStream(stream:DataStream | string) {
|
||||
const isObject = stream instanceof DataStream;
|
||||
if (isObject) {
|
||||
stream.Deactivate();
|
||||
this.remove(stream.name);
|
||||
} else {
|
||||
const dso = this.getByKey(stream);
|
||||
if (dso != null) {
|
||||
dso.Deactivate();
|
||||
}
|
||||
this.remove(stream);
|
||||
}
|
||||
ConsoleHelper.printStream(`Deleted stream [${isObject ? stream.name : stream}]`);
|
||||
}
|
||||
|
||||
public RemoveUserFromAllStreams(user:User) {
|
||||
for (let stream of this.getIterableItems()) {
|
||||
stream.RemoveUser(user);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { DataStream } from "./DataStream";
|
|||
import { UserArray } from "./UserArray";
|
||||
import { DataStreamArray } from "./DataStreamArray";
|
||||
import { ChatManager } from "../ChatManager";
|
||||
import { StatusUpdate } from "../packets/StatusUpdate";
|
||||
//const StatusUpdate = require("./Packets/StatusUpdate.js");
|
||||
|
||||
const rankingModes = [
|
||||
|
@ -146,7 +147,7 @@ export class User {
|
|||
else this.pp = 0;
|
||||
|
||||
if (userScoreUpdate || forceUpdate) {
|
||||
//StatusUpdate(this, this.id);
|
||||
StatusUpdate(this, this.id);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue