import { Channel } from 'channel/channel' ; import { ExtendedSocket } from 'extendedsocket' ; import { CSO2TakeDamageInfo , TDIKillFlags } from 'gametypes/cso2takedamageinfo' ; import { CSTeamNum , RoomTeamBalance , RoomGamemode } from 'gametypes/shareddefs' ; import { MatchProgress } from 'room/matchprogress' ; import { RoomSettings } from 'room/roomsettings' ; import { RoomUserEntry } from 'room/roomuserentry' ; import { InRoomUpdateSettings } from 'packets/in/room/updatesettings' ; import { OutHostPacket } from 'packets/out/host' ; import { OutRoomPacket } from 'packets/out/room' ; import { OutUdpPacket } from 'packets/out/udp' ; import { OutUserInfoPacket } from 'packets/out/userinfo' ; import { ActiveConnections } from 'storage/activeconnections' ; import { UserSession } from 'user/usersession' ; import { UserService } from 'services/userservice' ; export enum RoomReadyStatus { NotReady = 0 , Ingame = 1 , Ready = 2 } export interface IRoomOptions { roomName ? : string ; roomPassword ? : string ; gameModeId ? : number ; mapId ? : number ; winLimit ? : number ; killLimit ? : number ; startMoney ? : number ; forceCamera ? : number ; nextMapEnabled ? : number ; changeTeams ? : number ; enableBots ? : boolean ; difficulty ? : number ; respawnTime ? : number ; teamBalance ? : number ; weaponRestrictions ? : number ; hltvEnabled ? : number ; } export enum RoomStatus { Waiting = 1 , Ingame = 2 } const defaultCountdownNum = 7 ; export class Room { param user returns public static cleanUpUser ( conn ) { const session = conn . session ; if ( session == null ) { return false ; } const room = session . currentRoom ; if ( room == null ) { return false ; } room . removeUser ( session . user . id ) ; session . currentRoom = null ; return true ; } public id : number ; public settings : RoomSettings ; public host : RoomUserEntry ; public usersInfo : RoomUserEntry [ ] ; private emptyRoomCallback : ( emptyRoom : Room , channel : Channel ) => void ; private parentChannel : Channel ; private countingDown : boolean ; private countdown : number ; private ingameMatchProgress : MatchProgress ; constructor ( roomId , hostUserId , hostConn , parentChannel , emptyRoomCallback ? , options ? ) { this . id = roomId ; this . usersInfo = [ ] ; this . parentChannel = parentChannel ; this . emptyRoomCallback = emptyRoomCallback ; this . settings = new RoomSettings ( options ) ; this . countingDown = false ; this . countdown = defaultCountdownNum ; this . host = this . addUser ( hostUserId , hostConn ) ; this . ingameMatchProgress = new MatchProgress ( ) ; } returns public getFreeSlots ( ) { const availableSlots = this . settings . maxPlayers - this . usersInfo . length ; return Math . max ( 0 , availableSlots ) ; } returns public hasFreeSlots ( ) { return this . getFreeSlots ( ) !== 0 ; } returns public IsPasswordProtected ( ) { return ( this . settings . roomPassword != null && this . settings . roomPassword . length > 0 ) ; } param password returns public ComparePassword ( password ) { return this . settings . roomPassword === password ; } param userId returns public hasUser ( userId ) { return this . getRoomUser ( userId ) != null ; } param userId returns public addUser ( userId , connection ) { const userData = new RoomUserEntry ( userId , connection , this . findDesirableTeamNum ( ) , RoomReadyStatus . NotReady ) ; this . usersInfo . push ( userData ) ; return userData ; } param userId public removeUser ( userId ) { for ( const info of this . usersInfo ) { if ( info . userId === userId ) { this . usersInfo . splice ( this . usersInfo . indexOf ( info ) , 1 ) ; this . onUserRemoved ( info . userId ) ; return ; } } } returns public findDesirableTeamNum ( ) { let trNum = 0 ; let ctNum = 0 ; for ( const userData of this . usersInfo . values ( ) ) { const team = userData . team ; if ( team === CSTeamNum . Terrorist ) { trNum ++ ; } else if ( team === CSTeamNum . CounterTerrorist ) { ctNum ++ ; } else { console . warn ( 'Room::findDesirableTeamNum: Unknown team number' ) ; } } if ( this . settings . areBotsEnabled ) { if ( trNum + ctNum > 0 ) { const hostTeamNum = this . host . team ; return hostTeamNum ; } } if ( trNum < ctNum ) { return CSTeamNum . Terrorist ; } else { return CSTeamNum . CounterTerrorist ; } } param userId public getUserTeam ( userId ) { const info = this . getRoomUser ( userId ) ; if ( info == null ) { console . warn ( 'getUserTeam: userId %i not found' , userId ) ; return CSTeamNum . Unknown ; } return info . team ; } returns public getNumOfReadyRealPlayers ( ) { let numReadyPlayers = 0 ; for ( const info of this . usersInfo ) { if ( info . isReady ( ) === true || info === this . host ) { numReadyPlayers ++ ; } } return numReadyPlayers ; } /**
     * gives the number of elements in the counter terrorist team
     */ public getNumOfRealCts ( ) { let numCt = 0 ; for ( const userData of this . usersInfo . values ( ) ) { const team = userData . team ; if ( team === CSTeamNum . CounterTerrorist ) { numCt ++ ; } } return numCt ; } /**
     * gives the number of elements in the terrorist team
     */ public getNumOfRealTerrorists ( ) { let numTerrorists = 0 ; for ( const userData of this . usersInfo . values ( ) ) { const team = userData . team ; if ( team === CSTeamNum . Terrorist ) { numTerrorists ++ ; } } return numTerrorists ; } returns public getNumOfReadyPlayers ( ) { let botPlayers = this . settings . numCtBots + this . settings . numTrBots ; if ( this . settings . teamBalanceType === RoomTeamBalance . WithBots ) { const numCts = this . getNumOfRealCts ( ) ; const numTer = this . getNumOfRealTerrorists ( ) ; const requiredBalanceBots = Math . abs ( numCts - numTer ) ; botPlayers = Math . max ( botPlayers , requiredBalanceBots ) ; } return this . getNumOfReadyRealPlayers ( ) + botPlayers ; } param userId returns public getRoomUser ( userId ) { return this . usersInfo . find ( ( u ) => u . userId === userId ) ; } param userId public getUserReadyStatus ( user ) { return user . ready ; } param userId returns public isUserReady ( userId ) { const userInfo = this . getRoomUser ( userId ) ; if ( userInfo == null ) { console . warn ( `couldnt get user's room data (userId: ${ userId })` ) ; return false ; } return userInfo . ready === RoomReadyStatus . Ready ; } param userId returns public isUserIngame ( userId ) { const user = this . getRoomUser ( userId ) ; if ( user == null ) { return false ; } return user . isIngame ; } returns public isRoomReady ( ) { for ( const info of this . usersInfo ) { if ( info . isReady ( ) === false ) { return false ; } } return true ; } param user param newTeam public updateUserTeam ( userId , newTeam ) { const user = this . getRoomUser ( userId ) ; user . team = newTeam ; this . broadcastNewUserTeamNum ( userId , newTeam ) ; } param user param ingame public setUserIngame ( user , ingame ) { user . isIngame = ingame ; user . ready = ingame ? RoomReadyStatus . Ingame : RoomReadyStatus . NotReady ; } param userId param newStatus returns public toggleUserReadyStatus ( userId ) { if ( userId === this . host . userId ) { console . warn ( 'toggleUserReadyStatus: host tried to toggle ready' ) ; return null ; } const userInfo = this . getRoomUser ( userId ) ; if ( userInfo == null ) { console . warn ( 'toggleUserReadyStatus: couldnt get userinfo' ) ; return null ; } if ( userInfo . isIngame === true ) { console . warn ( `toggleUserReadyStatus: user ${ userId } tried to toggle ready status while ingame` ) ; return null ; } const curStatus = userInfo . ready ; const newStatus = curStatus === RoomReadyStatus . NotReady ? RoomReadyStatus . Ready : RoomReadyStatus . NotReady ; userInfo . ready = newStatus ; return newStatus ; } returns public resetIngameUsersReadyStatus ( ) { for ( const user of this . usersInfo ) { if ( user == null ) { console . warn ( 'resetIngameUsersReadyStatus: couldnt get userinfo' ) ; return false ; } user . ready = RoomReadyStatus . NotReady ; } return true ; } returns public getStatus ( ) { return this . settings . status ; } param newStatus public setStatus ( newStatus ) { this . settings . status = newStatus ; this . settings . isIngame = newStatus === RoomStatus . Ingame ; this . ingameMatchProgress . SetIngame ( this . settings . isIngame ) ; } param hostNextNum public progressCountdown ( hostNextNum ) { if ( this . countdown > defaultCountdownNum || this . countdown < 0 ) { console . warn ( 'Room: the saved countdown is invalid!' ) ; this . countdown = 0 ; } if ( this . countingDown === false ) { this . countingDown = true ; } this . countdown -- ; if ( this . countdown !== hostNextNum ) { console . warn ( `host's countdown does not match ours. ours ${ this . countdown } host ${ hostNextNum }` ) ; } } returns public getCountdown ( ) { if ( this . countingDown === false ) { console . warn ( 'getCountdown: tried to get countdown without counting down' + 'this is most likely a bug' ) ; return 0 ; } if ( this . countdown > defaultCountdownNum || this . countdown < 0 ) { console . warn ( `our countdown is out of bounds. it's ${ this . countdown }` ) ; this . countdown = 0 ; } return this . countdown ; } returns public isGlobalCountdownInProgress ( ) { return this . countingDown ; } /**
     * stops and resets the 'game start' countdown
     */ public stopCountdown ( ) { this . countingDown = false ; this . countdown = defaultCountdownNum ; } /**
     * ends a match and sends players to the match results screen
     */ public async endGame ( ) { this . setStatus ( RoomStatus . Waiting ) ; this . resetIngameUsersReadyStatus ( ) ; this . recurseUsers ( ( u ) : void => { this . sendRoomStatusTo ( u ) ; this . sendRoomUsersReadyStatusTo ( u ) ; if ( this . isUserIngame ( u . userId ) === true ) { this . sendGameEnd ( u ) ; this . setUserIngame ( u , false ) ; } } ) ; this . sendBroadcastReadyStatus ( ) ; await this . RewardUsers ( ) ; } returns public canStartGame ( ) { const gamemode = this . settings . gameModeId ; switch ( gamemode ) { case RoomGamemode . deathmatch : case RoomGamemode . original : case RoomGamemode . originalmr : case RoomGamemode . casualbomb : case RoomGamemode . casualoriginal : case RoomGamemode . eventmod01 : case RoomGamemode . eventmod02 : case RoomGamemode . diy : case RoomGamemode . campaign1 : case RoomGamemode . campaign2 : case RoomGamemode . campaign3 : case RoomGamemode . campaign4 : case RoomGamemode . campaign5 : case RoomGamemode . tdm_small : case RoomGamemode . de_small : case RoomGamemode . madcity : case RoomGamemode . madcity_team : case RoomGamemode . gunteamdeath : case RoomGamemode . gunteamdeath_re : case RoomGamemode . stealth : case RoomGamemode . teamdeath : case RoomGamemode . teamdeath_mutation : if ( this . getNumOfReadyPlayers ( ) < 2 ) { return false ; } break ; case RoomGamemode . giant : case RoomGamemode . hide : case RoomGamemode . hide2 : case RoomGamemode . hide_match : case RoomGamemode . hide_origin : case RoomGamemode . hide_Item : case RoomGamemode . hide_multi : case RoomGamemode . ghost : case RoomGamemode . pig : case RoomGamemode . tag : case RoomGamemode . zombie : case RoomGamemode . zombiecraft : case RoomGamemode . zombie_commander : case RoomGamemode . zombie_prop : case RoomGamemode . zombie_zeta : if ( this . getNumOfReadyRealPlayers ( ) < 2 ) { return false ; } break ; } return true ; } param fn public recurseUsers ( fn ) { for ( const user of this . usersInfo ) { fn ( user ) ; } } param fn public recurseNonHostUsers ( fn ) { for ( const user of this . usersInfo ) { if ( user !== this . host ) { fn ( user ) ; } } } param userId public sendJoinNewRoom ( userId ) { const user = this . getRoomUser ( userId ) ; user . conn . send ( OutRoomPacket . createAndJoin ( this ) ) ; } param newSettings public updateSettings ( newSettings ) { if ( newSettings . roomName != null ) { this . settings . roomName = newSettings . roomName ; } if ( newSettings . roomPassword != null ) { this . settings . roomPassword = newSettings . roomPassword ; } if ( newSettings . gameModeId != null ) { this . settings . gameModeId = newSettings . gameModeId ; } if ( newSettings . mapId != null ) { this . settings . mapId = newSettings . mapId ; } if ( newSettings . killLimit != null ) { this . settings . killLimit = newSettings . killLimit ; } if ( newSettings . winLimit != null ) { this . settings . winLimit = newSettings . winLimit ; } if ( newSettings . startMoney != null ) { this . settings . startMoney = newSettings . startMoney ; } if ( newSettings . maxPlayers != null ) { this . updateMaxPlayers ( newSettings . maxPlayers ) ; } if ( newSettings . respawnTime != null ) { this . settings . respawnTime = newSettings . respawnTime ; } if ( newSettings . changeTeams != null ) { this . settings . changeTeams = newSettings . changeTeams ; } if ( newSettings . forceCamera != null ) { this . settings . forceCamera = newSettings . forceCamera ; } if ( newSettings . teamBalanceType != null ) { this . settings . teamBalanceType = newSettings . teamBalanceType ; } if ( newSettings . weaponRestrictions != null ) { this . settings . weaponRestrictions = newSettings . weaponRestrictions ; } if ( newSettings . hltvEnabled != null ) { this . settings . hltvEnabled = newSettings . hltvEnabled ; } if ( newSettings . mapCycleType != null ) { this . settings . mapCycleType = newSettings . mapCycleType ; } if ( newSettings . multiMaps != null ) { this . settings . multiMaps = newSettings . multiMaps ; } if ( newSettings . botEnabled != null ) { this . updateBotEnabled ( newSettings . botEnabled , newSettings . botDifficulty , newSettings . numCtBots , newSettings . numTrBots ) ; } if ( newSettings . unk00 != null ) { this . settings . unk00 = newSettings . unk00 ; } if ( newSettings . unk01 != null ) { this . settings . unk01 = newSettings . unk01 ; } if ( newSettings . unk02 != null ) { this . settings . unk02 = newSettings . unk02 ; } if ( newSettings . unk03 != null ) { this . settings . unk03 = newSettings . unk03 ; } if ( newSettings . unk10 != null ) { this . settings . unk10 = newSettings . unk10 ; } if ( newSettings . unk13 != null ) { this . settings . unk13 = newSettings . unk13 ; } if ( newSettings . unk17 != null ) { this . settings . unk17 = newSettings . unk17 ; } if ( newSettings . unk18 != null ) { this . settings . unk18 = newSettings . unk18 ; } if ( newSettings . status != null ) { this . settings . status = newSettings . status ; } if ( newSettings . unk21 != null ) { this . settings . unk21 = newSettings . unk21 ; } if ( newSettings . unk23 != null ) { this . settings . unk23 = newSettings . unk23 ; } if ( newSettings . unk24 != null ) { this . settings . unk24 = newSettings . unk24 ; } if ( newSettings . unk25 != null ) { this . settings . unk25 = newSettings . unk25 ; } if ( newSettings . unk29 != null ) { this . settings . unk29 = newSettings . unk29 ; } if ( newSettings . unk30 != null ) { this . settings . unk30 = newSettings . unk30 ; } if ( newSettings . unk31 != null ) { this . settings . unk31 = newSettings . unk31 ; } if ( newSettings . unk32 != null ) { this . settings . unk32 = newSettings . unk32 ; } if ( newSettings . unk33 != null ) { this . settings . unk33 = newSettings . unk33 ; } if ( newSettings . unk35 != null ) { this . settings . unk35 = newSettings . unk35 ; } if ( newSettings . unk36 != null ) { this . settings . unk36 = newSettings . unk36 ; } if ( newSettings . unk37 != null ) { this . settings . unk37 = newSettings . unk37 ; } if ( newSettings . unk38 != null ) { this . settings . unk38 = newSettings . unk38 ; } if ( newSettings . unk39 != null ) { this . settings . unk39 = newSettings . unk39 ; } if ( newSettings . isIngame != null ) { this . settings . isIngame = newSettings . isIngame ; } if ( newSettings . unk43 != null ) { this . settings . unk43 = newSettings . unk43 ; } if ( newSettings . difficulty != null ) { this . settings . difficulty = newSettings . difficulty ; } this . broadcastNewRoomSettings ( ) ; } public onPlayerKilled ( damageInfo ) { console . debug ( this . GetDebugKillMessage ( damageInfo ) ) ; this . HandlePlayerKilled ( damageInfo ) ; this . HandlePlayerDeath ( damageInfo ) ; } public onUserAssisted ( userId , assists ) { const user = this . getRoomUser ( userId ) ; user . assists += assists ; } public onRoundWon ( winningTeam ) { this . ingameMatchProgress . ScoreTeam ( winningTeam ) ; console . debug ( this . ingameMatchProgress . GetDebugRoundEndMessage ( winningTeam ) ) ; } param user public sendRoomSettingsTo ( userId ) { const user = this . getRoomUser ( userId ) ; user . conn . send ( OutRoomPacket . updateSettings ( this . settings ) ) ; } public sendUpdateRoomSettingsTo ( user , updatedSettings ) { user . conn . send ( OutRoomPacket . updateSettings ( updatedSettings ) ) ; } param user param newUser public sendNewUserTo ( user , newUser ) { const team = newUser . team ; if ( team == null ) { console . warn ( 'sendNewUserTo: couldnt get user team (userId: %i room "%s" room id %i)' , user . userId , this . settings . roomName , this . id ) ; return null ; } user . conn . send ( OutRoomPacket . playerJoin ( newUser , team ) ) ; } param userId public updateNewPlayerReadyStatus ( userId ) { const target = this . getRoomUser ( userId ) ; this . recurseUsers ( ( u ) : void => { this . sendUserReadyStatusTo ( target , u ) ; if ( u !== target ) { this . sendNewUserTo ( u , target ) ; this . sendUserReadyStatusTo ( u , target ) ; } } ) ; } public hostGameStart ( ) { this . stopCountdown ( ) ; this . setStatus ( RoomStatus . Ingame ) ; this . setUserIngame ( this . host , true ) ; this . recurseNonHostUsers ( ( u ) : void => { this . sendRoomStatusTo ( u ) ; if ( u . isReady ( ) ) { this . setUserIngame ( u , true ) ; this . sendConnectHostTo ( u , this . host ) ; this . sendGuestDataTo ( this . host , u ) ; } } ) ; this . sendBroadcastReadyStatus ( ) ; this . sendStartMatchTo ( this . host ) ; } public guestGameJoin ( guestUserId ) { const guest = this . getRoomUser ( guestUserId ) ; this . sendRoomStatusTo ( guest ) ; this . setUserIngame ( guest , true ) ; this . sendConnectHostTo ( guest , this . host ) ; this . sendGuestDataTo ( this . host , guest ) ; this . sendBroadcastReadyStatus ( ) ; } param sourceUserId public broadcastNewUserReadyStatus ( sourceUserId ) { const sourceUser = this . getRoomUser ( sourceUserId ) ; this . recurseUsers ( ( u ) : void => { this . sendUserReadyStatusTo ( u , sourceUser ) ; } ) ; } /**
     * sends updated room settings to everyone in it
     */ public broadcastNewRoomSettings ( ) { this . recurseUsers ( ( u ) : void => { this . sendUpdateRoomSettingsTo ( u , this . settings ) ; } ) ; } param sourceUserId param newTeamNum public broadcastNewUserTeamNum ( sourceUserId , newTeamNum ) { const sourceUser = this . getRoomUser ( sourceUserId ) ; this . recurseUsers ( ( u ) : void => { this . sendTeamChangeTo ( u , sourceUser , newTeamNum ) ; if ( this . settings . areBotsEnabled ) { u . team = newTeamNum ; this . sendTeamChangeGlobal ( u , newTeamNum ) ; } } ) ; } param shouldCountdown public broadcastCountdown ( shouldCountdown ) { this . recurseUsers ( ( u ) : void => { this . sendCountdownTo ( u , shouldCountdown ) ; } ) ; } param user param player public sendUserReadyStatusTo ( user , player ) { const ready = player . ready ; if ( ready == null ) { console . warn ( 'sendUserReadyStatusTo: couldnt get user status (userId: %i room "%s" room id %i)' , user . userId , this . settings . roomName , this . id ) ; return null ; } user . conn . send ( OutRoomPacket . setUserReadyStatus ( player . userId , ready ) ) ; } param targetUser public sendRoomUsersReadyStatusTo ( targetUser ) { this . recurseUsers ( ( u ) : void => { this . sendUserReadyStatusTo ( targetUser , u ) ; } ) ; } /**
     * send everyone everyone's ready status
     */ public sendBroadcastReadyStatus ( ) { this . recurseUsers ( ( u ) : void => { this . sendRoomUsersReadyStatusTo ( u ) ; } ) ; } param user param host public sendConnectHostTo ( user , host ) { const hostSession = host . conn . session ; if ( hostSession == null ) { return ; } user . conn . send ( new OutUdpPacket ( true , host . userId , hostSession . externalNet . ipAddress , hostSession . externalNet . serverPort ) ) ; user . conn . send ( OutHostPacket . joinHost ( host . userId ) ) ; } param host param guest public sendGuestDataTo ( host , guest ) { const guestSession = guest . conn . session ; if ( guestSession == null ) { return ; } host . conn . send ( new OutUdpPacket ( false , guest . userId , guestSession . externalNet . ipAddress , guestSession . externalNet . clientPort ) ) ; } param host public sendStartMatchTo ( host ) { host . conn . send ( OutHostPacket . gameStart ( host . userId ) ) ; } param user param player param newTeamNum public sendTeamChangeTo ( user , player , newTeamNum ) { user . conn . send ( OutRoomPacket . setUserTeam ( player . userId , newTeamNum ) ) ; } param player param newTeamNum public sendTeamChangeGlobal ( player , newTeamNum ) { this . recurseUsers ( ( u ) => { this . sendTeamChangeTo ( u , player , newTeamNum ) ; } ) ; } param user param shouldCountdown public sendCountdownTo ( user , shouldCountdown ) { user . conn . send ( shouldCountdown ? OutRoomPacket . progressCountdown ( this . getCountdown ( ) ) : OutRoomPacket . stopCountdown ( ) ) ; } /**
     * ends a room's match and sends the players to the result window
     */ public sendGameEnd ( user ) { user . conn . send ( OutHostPacket . hostStop ( ) ) ; user . conn . send ( OutRoomPacket . setGameResult ( ) ) ; } param user public sendRoomStatusTo ( user ) { const settings = new RoomSettings ( ) ; settings . status = this . settings . status ; settings . isIngame = this . settings . isIngame ; user . conn . send ( OutRoomPacket . updateSettings ( settings ) ) ; } param newMaxPlayers private updateMaxPlayers ( newMaxPlayers ) { if ( this . settings . maxPlayers > newMaxPlayers ) { if ( this . settings . areBotsEnabled ) { this . settings . numCtBots = this . settings . numTrBots = 4 ; } } this . settings . maxPlayers = newMaxPlayers ; } param newMaxPlayers private updateBotEnabled ( botEnabled , botDifficulty , ctBots , terBots ) { this . settings . areBotsEnabled = ! ! botEnabled ; if ( this . settings . areBotsEnabled ) { this . settings . botDifficulty = botDifficulty ; this . settings . numCtBots = ctBots ; this . settings . numTrBots = terBots ; const hostTeamNum = this . host . team ; this . recurseNonHostUsers ( ( u ) : void => { const curUserTeamNum = this . getUserTeam ( u . userId ) ; if ( curUserTeamNum !== hostTeamNum ) { u . team = hostTeamNum ; this . sendTeamChangeGlobal ( u , hostTeamNum ) ; } } ) ; } else { this . settings . botDifficulty = 0 ; this . settings . numCtBots = 0 ; this . settings . numTrBots = 0 ; } } param userId private onUserRemoved ( userId ) { if ( this . usersInfo . length !== 0 ) { this . sendRemovedUser ( userId ) ; if ( userId === this . host . userId ) { this . findAndUpdateNewHost ( ) ; } } else { this . emptyRoomCallback ( this , this . parentChannel ) ; } } param deletedUserId private sendRemovedUser ( deletedUserId ) { for ( const info of this . usersInfo ) { info . conn . send ( OutRoomPacket . playerLeave ( deletedUserId ) ) ; } } param newHost private updateHost ( newHost ) { this . host = newHost ; for ( const user of this . usersInfo ) { user . conn . send ( OutRoomPacket . setHost ( this . host . userId ) ) ; } } returns private findAndUpdateNewHost ( ) { if ( this . usersInfo . length === 0 ) { return false ; } this . updateHost ( this . usersInfo [ 0 ] ) ; return true ; } private GetDebugKillMessage ( damageInfo ) { let attackerName = null ; let victimName = null ; const attackerConn = ActiveConnections . Singleton ( ) . FindByOwnerId ( damageInfo . attacker . userId ) ; const victimConn = ActiveConnections . Singleton ( ) . FindByOwnerId ( damageInfo . victim . userId ) ; if ( attackerConn != null ) { attackerName = attackerConn . session . user . playername ; } if ( victimConn != null ) { victimName = victimConn . session . user . playername ; } return `${ attackerName } killed ${ victimName } with ${ damageInfo . attacker . weaponId } (killFlags: ${ damageInfo . killFlags . toString ( 16 ) })` ; } private HandlePlayerKilled ( damageInfo ) { if ( damageInfo . attacker . userId === 0 ) { return ; } const attackerConn = ActiveConnections . Singleton ( ) . FindByOwnerId ( damageInfo . attacker . userId ) ; if ( attackerConn == null ) { console . warn ( 'Could not get the connection of the attacker' ) ; return ; } if ( attackerConn . session . currentRoom !== this ) { console . warn ( 'The attacker is not on the same room as the host' ) ; return ; } const attackerEntry = this . getRoomUser ( damageInfo . attacker . userId ) ; attackerEntry . kills ++ ; if ( damageInfo . killFlags & TDIKillFlags . KilledByHeadshot ) { attackerEntry . headshots ++ ; } } private HandlePlayerDeath ( damageInfo ) { if ( damageInfo . victim . userId === 0 ) { return ; } const victimConn = ActiveConnections . Singleton ( ) . FindByOwnerId ( damageInfo . victim . userId ) ; if ( victimConn == null ) { console . warn ( 'Could not get the connection of the victim' ) ; return ; } if ( victimConn . session . currentRoom !== this ) { console . warn ( 'The victim is not on the same room as the host' ) ; return ; } const roomUser = this . getRoomUser ( damageInfo . victim . userId ) ; roomUser . deaths ++ ; } private async RewardUsers ( ) { const updatePromises = [ ] ; const winnerTeam = this . ingameMatchProgress . GetWinningTeam ( ) ; for ( const userInfo of this . usersInfo ) { const userConn = userInfo . conn ; if ( userConn == null ) { console . warn ( `RewardUsers: user's connection with id ${ userInfo . userId } is null` ) ; continue ; } const user = userConn . session . user ; if ( user == null ) { console . warn ( `RewardUsers: got user's ${ userInfo . userId } connection but not the data?` ) ; continue ; } user . kills += userInfo . kills ; user . headshots += userInfo . headshots ; user . deaths += userInfo . deaths ; user . assists += userInfo . assists ; user . played_matches ++ ; if ( userInfo . team === winnerTeam ) { user . wins ++ ; } updatePromises . push ( UserService . UpdatePartial ( { kills : user . kills , headshots : user . headshots , deaths : user . deaths , assists : user . assists , played_matches : user . played_matches , wins : user . wins } , user . id ) ) ; userConn . send ( OutUserInfoPacket . updateGameStats ( user ) ) ; } const updateRes = await Promise . all ( updatePromises ) ; for ( const res of updateRes ) { if ( res === false ) { console . warn ( 'RewardUsers: failed to update an user' ) ; } } } }