2020-08-27 13:09:35 +01:00
const osu = require ( "osu-packet" ) ,
2020-09-02 10:14:10 +01:00
fs = require ( "fs" ) ,
2020-08-27 13:09:35 +01:00
packetIDs = require ( "./packetIDs.json" ) ,
loginHandler = require ( "./loginHandler.js" ) ,
parseUserData = require ( "./util/parseUserData.js" ) ,
userManager = require ( "./userManager.js" ) ,
User = require ( "./User.js" ) ,
bakedResponses = require ( "./bakedResponses.js" ) ,
Streams = require ( "./Streams.js" ) ;
global . users = [
2020-09-02 12:17:22 +01:00
new User ( 3 , "SillyBot" , "SillyBot" , new Date ( ) . getTime ( ) )
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 ( ( ) => {
for ( let i = 0 ; i < global . users . length ; i ++ ) {
const User = global . users [ i ] ;
if ( User . id == 3 ) continue ; // Ignore the bot
// Bot: :(
User . getNewUserInformationFromDatabase ( ) ;
}
} , 10000 ) ;
// Set the bot's position on the map
global . users [ 0 ] . location [ 0 ] = 50 ;
global . users [ 0 ] . location [ 1 ] = - 32 ;
// An array containing all currently active multiplayer matches
global . matches = [ ] ;
// An array containing the last 15 messages in chat
// TODO: Bother making this
global . chatHistory = [ ] ;
global . StreamsHandler = new Streams ( ) ;
// An array containing all chat channels
// TODO: Send user chat channels and not have osu! crash
global . channels = [
{ channelName : "#osu" , channelTopic : "The main channel" , channelUserCount : 0 } ,
{ channelName : "#userlog" , channelTopic : "Log about stuff doing go on yes very" , channelUserCount : 0 } ,
{ channelName : "#lobby" , channelTopic : "Talk about multiplayer stuff" , channelUserCount : 0 } ,
{ channelName : "#english" , channelTopic : "Talk in exclusively English" , channelUserCount : 0 } ,
{ channelName : "#japanese" , channelTopic : "Talk in exclusively Japanese" , channelUserCount : 0 } ,
] ;
// 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 ( ( ) => {
global . usersOnline = ( global . users . length - 1 ) ;
global . multiplayerMatches = [ global . matches . length , 0 ] ; // TODO: Respect private matches
fs . appendFile (
"server-stats.log" ,
` ${ global . usersOnline } | ${ global . multiplayerMatches [ 0 ] } , ${ global . multiplayerMatches [ 1 ] } | ${ global . httpRequestsPerLogInterval } | ${ new Date ( ) . getTime ( ) } @ ` ,
( ) => { }
) ;
global . usersOnline = 0 ;
global . multiplayerMatches = [ 0 , 0 ] ;
global . httpRequestsPerLogInterval = 0 ;
} , logInterval * 1000 ) ;
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" ) ,
2020-08-27 13:09:35 +01:00
Multiplayer = require ( "./Multiplayer.js" ) ,
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" ) ,
MultiplayerInvite = require ( "./Packets/MultiplayerInvite.js" ) ;
2020-08-27 13:09:35 +01:00
module . exports = 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
let responseTokenString = "" ,
responseData = new Buffer . alloc ( 0 ) ;
// 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 } ] ` ) ;
loginHandler ( req , res , userData ) ;
} else {
// Client has a token, let's see what they want.
try {
// Get the current user
const userClass = userManager . getUserFromToken ( requestTokenString ) ;
// Make sure the client's token isn't invalid
if ( userClass != null ) {
// 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 ] ;
// Create a new bancho packet writer per packet
const osuPacketWriter = new osu . Bancho . Writer ;
switch ( CurrentPacket . id ) {
case packetIDs . client _changeAction :
ChangeAction ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _sendPublicMessage :
SendPublicMessage ( CurrentPacket , userClass ) ;
break ;
case packetIDs . client _logout :
Logout ( userClass ) ;
break ;
case packetIDs . client _requestStatusUpdate :
UserPresenceBundle ( userClass ) ;
break ;
case packetIDs . client _pong :
break ;
case packetIDs . client _startSpectating :
Spectator . startSpectatingUser ( CurrentPacket . data , userClass ) ;
break ;
case packetIDs . client _spectateFrames :
Spectator . sendSpectatorFrames ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _stopSpectating :
Spectator . stopSpectatingUser ( userClass ) ;
break ;
2020-09-02 10:57:12 +01:00
case packetIDs . client _sendPrivateMessage :
SendPrivateMessage ( CurrentPacket , userClass ) ;
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _joinLobby :
Multiplayer . userEnterLobby ( userClass ) ;
break ;
case packetIDs . client _createMatch :
Multiplayer . createMultiplayerMatch ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _joinMatch :
Multiplayer . joinMultiplayerMatch ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchChangeSlot :
Multiplayer . moveToSlot ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchReady :
Multiplayer . setReadyState ( userClass , true ) ;
break ;
case packetIDs . client _matchChangeSettings :
Multiplayer . updateMatch ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchNotReady :
Multiplayer . setReadyState ( userClass , false ) ;
break ;
case packetIDs . client _partMatch :
Multiplayer . leaveMatch ( userClass ) ;
break ;
case packetIDs . client _matchLock : // Also handles user kick
Multiplayer . kickPlayer ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchNoBeatmap :
Multiplayer . missingBeatmap ( userClass , true ) ;
break ;
case packetIDs . client _matchHasBeatmap :
Multiplayer . missingBeatmap ( userClass , false ) ;
break ;
case packetIDs . client _matchTransferHost :
Multiplayer . transferHost ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchChangeMods :
Multiplayer . updateMods ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchStart :
Multiplayer . startMatch ( userClass ) ;
break ;
case packetIDs . client _matchLoadComplete :
Multiplayer . setPlayerLoaded ( userClass ) ;
break ;
case packetIDs . client _matchComplete :
Multiplayer . onPlayerFinishMatch ( userClass ) ;
break ;
case packetIDs . client _matchScoreUpdate :
Multiplayer . updatePlayerScore ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _matchFailed :
break ;
case packetIDs . client _channelJoin :
// TODO: Implement user channel joining
// Auto channel joining is already complete
break ;
case packetIDs . client _channelPart :
ChannelPart ( userClass , CurrentPacket . data ) ;
break ;
2020-09-02 12:19:19 +01:00
case packetIDs . client _friendAdd :
AddFriend ( userClass , CurrentPacket . data ) ;
break ;
case packetIDs . client _friendRemove :
RemoveFriend ( userClass , CurrentPacket . data ) ;
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _userStatsRequest :
UserStatsRequest ( userClass , CurrentPacket . data ) ;
break ;
2020-09-02 11:31:06 +01:00
case packetIDs . client _invite :
MultiplayerInvite ( userClass , CurrentPacket . data ) ;
break ;
2020-08-27 13:09:35 +01:00
case packetIDs . client _userPresenceRequest :
UserPresence ( userClass , userClass . id ) ;
break ;
default :
// Ignore client_beatmapInfoRequest and client_receiveUpdates
if ( CurrentPacket . id == 68 || CurrentPacket . id == 79 ) break ;
// Print out unimplemented packet
console . dir ( CurrentPacket ) ;
break ;
}
// Put current user queue into response data
responseData = Buffer . concat ( [ responseData , userClass . queue ] , responseData . length + userClass . queue . length ) ;
userClass . clearQueue ( ) ;
}
} 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 , {
"cho-protocol" : 19 ,
"Connection" : "keep-alive" ,
"Keep-Alive" : "timeout=5, max=100" ,
"Content-Type" : "text/html; charset=UTF-8"
} ) ;
res . end ( responseData ) ;
}
}
} ;