2020-08-27 13:09:35 +01:00
const osu = require ( "osu-packet" ) ,
2020-09-02 10:14:10 +01:00
fs = require ( "fs" ) ,
2021-09-22 19:13:56 +01:00
packetIDs = require ( "./packetIDs.js" ) ,
2020-08-27 13:09:35 +01:00
loginHandler = require ( "./loginHandler.js" ) ,
parseUserData = require ( "./util/parseUserData.js" ) ,
User = require ( "./User.js" ) ,
2020-09-07 17:35:37 +01:00
getUserFromToken = require ( "./util/getUserByToken.js" ) ,
2020-08-27 13:09:35 +01:00
bakedResponses = require ( "./bakedResponses.js" ) ,
2020-09-07 19:23:06 +01:00
Streams = require ( "./Streams.js" ) ,
DatabaseHelperClass = require ( "./DatabaseHelper.js" ) ,
config = require ( "../config.json" ) ;
2020-08-27 13:09:35 +01:00
2021-05-15 00:24:39 +01:00
global . users = { } ;
global . userKeys = Object . keys ( global . users ) ;
global . addUser = function ( uuid , userToAdd ) {
global . users [ uuid ] = userToAdd ;
global . refreshUserKeys ( ) ;
}
global . removeUser = function ( userToRemove ) {
delete userToRemove ;
global . refreshUserKeys ( ) ;
}
global . refreshUserKeys = function ( ) {
global . userKeys = Object . keys ( global . users ) ;
}
2020-08-27 13:09:35 +01:00
2021-05-15 00:24:39 +01:00
// Add the bot user
global . addUser ( "bot" , new User ( 3 , "SillyBot" , "bot" , new Date ( ) . getTime ( ) ) ) ;
2020-09-07 19:23:06 +01:00
// Set the bot's position on the map
2021-05-15 00:24:39 +01:00
global . users [ "bot" ] . location [ 0 ] = 50 ;
global . users [ "bot" ] . location [ 1 ] = - 32 ;
2020-09-07 19:23:06 +01:00
2021-02-13 02:42:59 +00:00
global . DatabaseHelper = new DatabaseHelperClass ( config . databaseAddress , config . databasePort , config . databaseUsername , config . databasePassword , config . databaseName ) ;
2020-09-07 19:23:06 +01:00
2020-08-27 13:09:35 +01:00
// Start a loop that gets new data for users from the database for use on the user panel
2020-09-02 10:14:47 +01:00
// TODO: Some way of informing bancho that a user has set a score so details can be pulled down quickly
// Possible solution, TCP socket between the score submit server and bancho? redis? (score submit is on a different server, redis probably wouldn't work)
2020-08-27 13:09:35 +01:00
setInterval ( ( ) => {
2021-05-15 00:24:39 +01:00
for ( let i = 0 ; i < global . userKeys . length ; i ++ ) {
const User = global . users [ global . userKeys [ i ] ] ;
2020-08-27 13:09:35 +01:00
if ( User . id == 3 ) continue ; // Ignore the bot
// Bot: :(
User . getNewUserInformationFromDatabase ( ) ;
}
} , 10000 ) ;
// An array containing the last 15 messages in chat
// TODO: Bother making this
global . chatHistory = [ ] ;
2021-01-26 12:26:46 +00:00
global . addChatMessage = function ( msg ) {
if ( global . chatHistory . length == 15 ) {
global . chatHistory . splice ( 0 , 1 ) ;
global . chatHistory . push ( msg ) ;
} else {
global . chatHistory . push ( msg ) ;
}
}
2020-08-27 13:09:35 +01:00
global . StreamsHandler = new Streams ( ) ;
// An array containing all chat channels
// TODO: Send user chat channels and not have osu! crash
global . channels = [
2020-11-03 02:08:57 +00:00
{ 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 } ,
2020-08-27 13:09:35 +01:00
] ;
// Create a stream for each chat channel
for ( let i = 0 ; i < global . channels . length ; i ++ ) {
global . StreamsHandler . addStream ( global . channels [ i ] . channelName , false ) ;
}
// Add a stream for the multiplayer lobby
global . StreamsHandler . addStream ( "multiplayer_lobby" , false ) ;
// Start stream checking interval
global . StreamsHandler . streamChecker ( 5000 ) ;
2020-09-02 10:14:10 +01:00
// Server stats
global . usersOnline = 0 ;
global . multiplayerMatches = [ 0 , 0 ]
global . httpRequestsPerLogInterval = 0 ;
const logInterval = 10 ; // Secs
setInterval ( ( ) => {
2021-05-15 00:24:39 +01:00
global . usersOnline = ( global . userKeys . length - 1 ) ;
2021-01-26 12:26:46 +00:00
global . multiplayerMatches = [ global . MultiplayerManager . matches . length , 0 ] ; // TODO: Respect private matches
2020-09-02 10:14:10 +01:00
fs . appendFile (
"server-stats.log" ,
` ${ global . usersOnline } | ${ global . multiplayerMatches [ 0 ] } , ${ global . multiplayerMatches [ 1 ] } | ${ global . httpRequestsPerLogInterval } | ${ new Date ( ) . getTime ( ) } @ ` ,
2021-02-12 23:36:35 +00:00
( ) => { }
2020-09-02 10:14:10 +01:00
) ;
global . httpRequestsPerLogInterval = 0 ;
} , logInterval * 1000 ) ;
2021-01-26 12:26:46 +00:00
if ( ! fs . existsSync ( "tHMM.ds" ) ) fs . writeFileSync ( "tHMM.ds" , "0" ) ;
global . totalHistoricalMultiplayerMatches = parseInt ( fs . readFileSync ( "tHMM.ds" ) . toString ( ) ) ;
global . getAndAddToHistoricalMultiplayerMatches = function ( ) {
global . totalHistoricalMultiplayerMatches ++ ;
2021-05-15 00:24:39 +01:00
fs . writeFile ( "tHMM.ds" , ` ${ global . totalHistoricalMultiplayerMatches } ` , ( ) => { } ) ;
2021-01-26 12:26:46 +00:00
return global . totalHistoricalMultiplayerMatches ;
}
2020-08-27 13:09:35 +01:00
// Include packets
const ChangeAction = require ( "./Packets/ChangeAction.js" ) ,
SendPublicMessage = require ( "./Packets/SendPublicMessage.js" ) ,
Logout = require ( "./Packets/Logout.js" ) ,
Spectator = require ( "./Spectator.js" ) ,
2020-09-02 10:57:12 +01:00
SendPrivateMessage = require ( "./Packets/SendPrivateMessage.js" ) ,
2021-01-26 12:26:46 +00:00
MultiplayerManager = require ( "./MultiplayerManager.js" ) ,
ChannelJoin = require ( "./Packets/ChannelJoin.js" ) ,
2020-08-27 13:09:35 +01:00
ChannelPart = require ( "./Packets/ChannelPart.js" ) ,
2020-09-02 12:19:19 +01:00
AddFriend = require ( "./Packets/AddFriend.js" ) ,
RemoveFriend = require ( "./Packets/RemoveFriend.js" ) ,
2020-08-27 13:09:35 +01:00
UserPresenceBundle = require ( "./Packets/UserPresenceBundle.js" ) ,
UserPresence = require ( "./Packets/UserPresence.js" ) ,
2020-09-02 11:31:06 +01:00
UserStatsRequest = require ( "./Packets/UserStatsRequest.js" ) ,
2021-02-01 03:01:37 +00:00
MultiplayerInvite = require ( "./Packets/MultiplayerInvite.js" ) ,
TourneyMatchSpecialInfo = require ( "./Packets/TourneyMatchSpecialInfo.js" ) ,
2021-02-05 03:18:24 +00:00
TourneyMatchJoinChannel = require ( "./Packets/TourneyMatchSpecialInfo.js" ) ,
TourneyMatchLeaveChannel = require ( "./Packets/TourneyLeaveMatchChannel.js" ) ;
2020-08-27 13:09:35 +01:00
2021-01-26 12:26:46 +00:00
// A class for managing everything multiplayer
global . MultiplayerManager = new MultiplayerManager ( ) ;
2021-02-13 02:15:25 +00:00
module . exports = async function ( req , res ) {
2020-09-02 10:14:10 +01:00
// Add to requests for logging
global . httpRequestsPerLogInterval ++ ;
2020-08-27 13:09:35 +01:00
// Get the client's token string and request data
const requestTokenString = req . header ( "osu-token" ) ,
requestData = req . packet ;
// Server's response & new client token
2021-02-12 23:36:35 +00:00
let responseData = new Buffer . alloc ( 0 ) ;
2020-08-27 13:09:35 +01:00
// Check if the user is logged in
if ( requestTokenString == null ) {
// Client doesn't have a token yet, let's auth them!
const userData = parseUserData ( requestData ) ;
global . consoleHelper . printBancho ( ` New client connection. [User: ${ userData . username } ] ` ) ;
2021-02-13 02:42:59 +00:00
await loginHandler ( req , res , userData ) ;
2020-08-27 13:09:35 +01:00
} else {
// Client has a token, let's see what they want.
try {
// Get the current user
2021-01-26 12:26:46 +00:00
const PacketUser = getUserFromToken ( requestTokenString ) ;
2020-08-27 13:09:35 +01:00
// Make sure the client's token isn't invalid
2021-01-26 12:26:46 +00:00
if ( PacketUser != null ) {
2020-08-27 13:09:35 +01:00
// Create a new osu! packet reader
const osuPacketReader = new osu . Client . Reader ( requestData ) ;
// Parse current bancho packet
const PacketData = osuPacketReader . Parse ( ) ;
// Loop through parsed packet data
for ( let i = 0 ; i < PacketData . length ; i ++ ) {
// Get current packet
let CurrentPacket = PacketData [ i ] ;
2021-01-26 12:26:46 +00:00
// This is getting a little big, swap this out for mapped functions?
// Would require some standardisation
2020-08-27 13:09:35 +01:00
switch ( CurrentPacket . id ) {
case packetIDs . client _changeAction :
2021-01-26 12:26:46 +00:00
ChangeAction ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _sendPublicMessage :
2021-01-26 12:26:46 +00:00
SendPublicMessage ( PacketUser , CurrentPacket ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _logout :
2021-01-26 12:26:46 +00:00
Logout ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _requestStatusUpdate :
2021-01-26 12:26:46 +00:00
UserPresenceBundle ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2020-11-03 02:08:57 +00:00
case packetIDs . client _pong : // Pretty sure this is just a client ping
// so we probably don't do anything here
2021-01-26 12:26:46 +00:00
break ; // It's probably just the client wanting to pull data down.
2020-08-27 13:09:35 +01:00
case packetIDs . client _startSpectating :
2021-01-26 12:26:46 +00:00
Spectator . startSpectatingUser ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _spectateFrames :
2021-01-26 12:26:46 +00:00
Spectator . sendSpectatorFrames ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _stopSpectating :
2021-01-26 12:26:46 +00:00
Spectator . stopSpectatingUser ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2020-09-02 10:57:12 +01:00
case packetIDs . client _sendPrivateMessage :
2021-01-26 12:26:46 +00:00
SendPrivateMessage ( PacketUser , CurrentPacket ) ;
2020-09-02 10:57:12 +01:00
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _joinLobby :
2021-01-26 12:26:46 +00:00
global . MultiplayerManager . userEnterLobby ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2020-11-03 02:08:57 +00:00
case packetIDs . client _partLobby :
2021-01-26 12:26:46 +00:00
global . MultiplayerManager . userLeaveLobby ( PacketUser ) ;
2020-11-03 02:08:57 +00:00
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _createMatch :
2021-01-26 12:26:46 +00:00
global . MultiplayerManager . createMultiplayerMatch ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _joinMatch :
2021-01-26 12:26:46 +00:00
global . MultiplayerManager . joinMultiplayerMatch ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchChangeSlot :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . moveToSlot ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchReady :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . setReadyState ( PacketUser , true ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchChangeSettings :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . updateMatch ( CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchNotReady :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . setReadyState ( PacketUser , false ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _partMatch :
2021-01-26 12:26:46 +00:00
global . MultiplayerManager . leaveMultiplayerMatch ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2021-01-26 12:26:46 +00:00
// Also handles user kick if the slot has a user
case packetIDs . client _matchLock :
PacketUser . currentMatch . lockMatchSlot ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchNoBeatmap :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . missingBeatmap ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2020-11-03 02:08:57 +00:00
case packetIDs . client _matchSkipRequest :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . matchSkip ( PacketUser ) ;
2020-11-03 02:08:57 +00:00
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _matchHasBeatmap :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . notMissingBeatmap ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchTransferHost :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . transferHost ( CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchChangeMods :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . updateMods ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchStart :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . startMatch ( ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchLoadComplete :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . matchPlayerLoaded ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchComplete :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . onPlayerFinishMatch ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchScoreUpdate :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . updatePlayerScore ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _matchFailed :
2021-01-26 12:26:46 +00:00
PacketUser . currentMatch . matchFailed ( PacketUser ) ;
2020-08-27 13:09:35 +01:00
break ;
2021-02-01 03:01:37 +00:00
case packetIDs . client _matchChangeTeam :
PacketUser . currentMatch . changeTeam ( PacketUser ) ;
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _channelJoin :
2021-01-26 12:26:46 +00:00
ChannelJoin ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
case packetIDs . client _channelPart :
2021-01-26 12:26:46 +00:00
ChannelPart ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
2020-09-02 12:19:19 +01:00
case packetIDs . client _friendAdd :
2021-01-26 12:26:46 +00:00
AddFriend ( PacketUser , CurrentPacket . data ) ;
2020-09-02 12:19:19 +01:00
break ;
case packetIDs . client _friendRemove :
2021-01-26 12:26:46 +00:00
RemoveFriend ( PacketUser , CurrentPacket . data ) ;
2020-09-02 12:19:19 +01:00
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _userStatsRequest :
2021-01-26 12:26:46 +00:00
UserStatsRequest ( PacketUser , CurrentPacket . data ) ;
2020-08-27 13:09:35 +01:00
break ;
2021-02-01 03:01:37 +00:00
case packetIDs . client _specialMatchInfoRequest :
TourneyMatchSpecialInfo ( PacketUser , CurrentPacket . data ) ;
break ;
case packetIDs . client _specialJoinMatchChannel :
TourneyMatchJoinChannel ( PacketUser , CurrentPacket . data ) ;
break ;
2021-02-05 03:18:24 +00:00
case packetIDs . client _specialLeaveMatchChannel :
TourneyMatchLeaveChannel ( PacketUser , CurrentPacket . data ) ;
break ;
2020-09-02 11:31:06 +01:00
case packetIDs . client _invite :
2021-01-26 12:26:46 +00:00
MultiplayerInvite ( PacketUser , CurrentPacket . data ) ;
2020-09-02 11:31:06 +01:00
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _userPresenceRequest :
2021-01-26 12:26:46 +00:00
UserPresence ( PacketUser , PacketUser . id ) ;
2020-08-27 13:09:35 +01:00
break ;
default :
// Ignore client_beatmapInfoRequest and client_receiveUpdates
if ( CurrentPacket . id == 68 || CurrentPacket . id == 79 ) break ;
// Print out unimplemented packet
console . dir ( CurrentPacket ) ;
break ;
}
2021-01-26 12:26:46 +00:00
// Concat current user queue into response data
responseData = Buffer . concat ( [ responseData , PacketUser . queue ] , responseData . length + PacketUser . queue . length ) ;
PacketUser . clearQueue ( ) ;
2020-08-27 13:09:35 +01:00
}
} else {
// User's token is invlid, force a reconnect
global . consoleHelper . printBancho ( ` Forced client re-login (Token is invalid) ` ) ;
responseData = bakedResponses ( "reconnect" ) ;
}
} catch ( e ) {
console . error ( e ) ;
} finally {
// Send the prepared packet to the client
res . writeHead ( 200 , {
2021-02-12 23:36:35 +00:00
"cho-protocol" : global . protocolVersion ,
2020-08-27 13:09:35 +01:00
"Connection" : "keep-alive" ,
"Keep-Alive" : "timeout=5, max=100" ,
"Content-Type" : "text/html; charset=UTF-8"
} ) ;
res . end ( responseData ) ;
}
}
2021-01-26 12:26:46 +00:00
} ;