namespace KGS { export interface DatabaseDictionary < T > { [ key ] : T ; [ key ] : T ; } export class Database { public username : string ; public joinedChannelIds : number [ ] ; public channels : DatabaseDictionary < Models . Channel > ; public users : { [ name ] : Models . User ; } ; public get userNames ( ) : string [ ] { return Object . keys ( this . users ) ; } public games : DatabaseDictionary < Models . GameState > ; public automatch : Models . AutomatchState ; } class DatabaseInternal extends KGS . Database { constructor ( ) { super ( ) ; this . reinitialise ( ) ; } public reinitialise ( ) { this . username = null ; this . joinedChannelIds = [ ] ; this . channels = { } ; this . users = { } ; this . games = { } ; this . automatch = new Models . AutomatchState ( ) ; } public _createChannel ( digest , channelId , channelType ) { let channel = this . channels [ channelId ] ; if ( ! channel ) { channel = Models . Channel . createChannel ( channelId , channelType ) ; this . channels [ channelId ] = channel ; digest . touchChannel ( channelId ) ; } else if ( channel . channelType != channelType ) { throw "KGS Database: channel " + channelId . toString ( ) + " has unexpected channel type: " + channel . channelType . toString ( ) + " (expected: " + channelType . toString ( ) + ")" ; } return channel ; } public _requireChannel ( channelId , channelType ? ) { let channel = this . channels [ channelId ] ; if ( ! channel ) throw "KGS Database: channel " + channelId . toString ( ) + " not found" ; else if ( ( channelType != null ) && ( channel . channelType != channelType ) ) { throw "KGS Database: channel " + channelId . toString ( ) + " has unexpected channel type: " + channel . channelType . toString ( ) + " (expected: " + channelType . toString ( ) + ")" ; } return channel ; } public _updateUser ( digest , user ) { let record = this . users [ user . name ] ; let touchUser = false ; if ( ! record ) { this . users [ user . name ] = record = new Models . User ( user ) ; touchUser = true ; } else touchUser = record . mergeUser ( user ) ; if ( touchUser ) digest . touchUser ( user . name ) ; return record ; } public _updateUsers ( digest , users ) { for ( let i = 0 ; i < users . length ; ++ i ) { this . _updateUser ( digest , users [ i ] ) ; } } public _updateGameChannel ( digest , channelId , game ) { if ( ( < KGS . GameChannel | KGS . ChallengeChannel > game ) . roomId != null ) { let parentChannelId = ( < KGS . GameChannel | KGS . ChallengeChannel > game ) . roomId ; let parentChannel = this . channels [ parentChannelId ] ; if ( ( parentChannel != null ) && ( parentChannel . channelType == Models . ChannelType . Room ) ) { if ( ( < Models . RoomChannel > parentChannel ) . addGame ( channelId ) ) digest . touchChannelGames ( parentChannelId ) ; } } let gameChannel = this . _createChannel ( digest , channelId , Models . ChannelType . Game ) as Models . GameChannel ; if ( gameChannel . mergeGameChannel ( game ) ) digest . touchChannel ( gameChannel . channelId ) ; if ( game . players ) { if ( game . players . owner ) { this . _updateUser ( digest , game . players . owner ) ; if ( gameChannel . syncOwners ( [ game . players . owner ] ) ) digest . touchChannelOwners ( gameChannel . channelId ) ; } if ( game . players . white ) this . _updateUser ( digest , game . players . white ) ; if ( game . players . black ) this . _updateUser ( digest , game . players . black ) ; } } public _createGameState ( digest , channelId ) { let gameState = this . games [ channelId ] ; if ( ! gameState ) { let gameChannel = this . _requireChannel ( channelId , Models . ChannelType . Game ) as Models . GameChannel ; if ( gameChannel . size ) gameState = new Models . GameState ( gameChannel . size ) ; else gameState = new Models . GameState ( digest . perfstamp ) ; this . games [ channelId ] = gameState ; digest . touchGameTree ( channelId ) ; digest . touchGameClocks ( channelId ) ; digest . touchGameActions ( channelId ) ; } return gameState ; } public _unjoinChannel ( digest , channelId , closeChannel ) { if ( Utils . setRemove ( this . joinedChannelIds , channelId ) ) { digest . joinedChannelIds = true ; } if ( closeChannel ) { delete this . channels [ channelId ] ; } delete this . games [ channelId ] ; } } export class _Database { private _database : DatabaseInternal ; public database : KGS . Database ; constructor ( ) { $db = this . database = this . _database = new DatabaseInternal ( ) ; this . LOGOUT ( null , null ) ; } public LOGIN_SUCCESS = ( digest , message ) => { let you = this . _database . _updateUser ( digest , message . you ) ; if ( you . name != this . _database . username ) { this . _database . username = you . name ; digest . username = true ; } } ; public LOGOUT = ( digest , message ) => { this . _database . reinitialise ( ) ; } ; public ROOM_NAMES = ( digest , message ) => { if ( ! message . rooms ) return ; for ( let i = 0 ; i < message . rooms . length ; ++ i ) { let room = message . rooms [ i ] ; let channel = this . _database . _createChannel ( digest , room . channelId , Models . ChannelType . Room ) as Models . RoomChannel ; if ( channel . mergeRoomName ( room ) ) digest . touchChannel ( room . channelId ) ; } } ; public ROOM_DESC = ( digest , message ) => { let channel = this . _database . _createChannel ( digest , message . channelId , Models . ChannelType . Room ) as Models . RoomChannel ; if ( channel . mergeRoomDescription ( message . description ) ) digest . touchChannel ( message . channelId ) ; this . _database . _updateUsers ( digest , message . owners ) ; if ( channel . syncOwners ( message . owners ) ) digest . touchChannelOwners ( message . channelId ) ; } ; public ROOM_JOIN = ( digest , message ) => { let channel = this . _database . _createChannel ( digest , message . channelId , Models . ChannelType . Room ) ; this . _database . _updateUsers ( digest , message . users ) ; if ( channel . mergeUsers ( message . users ) ) digest . touchChannelUsers ( message . channelId ) ; this . GAME_LIST ( digest , message ) ; } ; public GAME_JOIN = ( digest , message ) => { if ( message . gameSummary ) { this . _database . _updateGameChannel ( digest , message . channelId , message . gameSummary ) ; } this . GAME_UPDATE ( digest , message ) ; this . GAME_STATE ( digest , message ) ; this . _database . _updateUsers ( digest , message . users ) ; let channel = this . _database . _createChannel ( digest , message . channelId , Models . ChannelType . Game ) as Models . GameChannel ; if ( channel . mergeUsers ( message . users ) ) digest . touchChannelUsers ( message . channelId ) ; } ; public CHALLENGE_JOIN = ( digest , message ) => { let channel = this . _database . _createChannel ( digest , message . channelId , Models . ChannelType . Game ) ; this . _database . _updateUsers ( digest , message . users ) ; if ( channel . mergeUsers ( message . users ) ) digest . touchChannelUsers ( message . channelId ) ; } ; public JOIN_COMPLETE = ( digest , message ) => { let channel = this . _database . _requireChannel ( message . channelId ) ; if ( Utils . setAdd ( this . _database . joinedChannelIds , channel . channelId ) ) { digest . joinedChannelIds = true ; digest . touchChannel ( message . channelId ) ; } } ; public UNJOIN = ( digest , message ) => { this . _database . _unjoinChannel ( digest , message . channelId , false ) ; } ; public CLOSE = ( digest , message ) => { this . _database . _unjoinChannel ( digest , message . channelId , true ) ; } ; public PRIVATE_KEEP_OUT = ( digest , message ) => { if ( ! digest . joinFailedChannelIds ) { digest . joinFailedChannelIds = [ message . channelId ] ; } else { digest . joinFailedChannelIds . push ( message . channelId ) ; } } ; public CHAT = ( digest , message ) => { this . _database . _updateUser ( digest , message . user ) ; let channel = this . _database . _requireChannel ( message . channelId ) ; if ( channel . appendChat ( message , Models . ChatImportance . Chat , digest . timestamp ) ) digest . touchChannelChat ( message . channelId ) ; } ; public ANNOUNCE = ( digest , message ) => { this . _database . _updateUser ( digest , message . user ) ; let channel = this . _database . _requireChannel ( message . channelId ) ; if ( channel . appendChat ( message , Models . ChatImportance . Announcement , digest . timestamp ) ) digest . touchChannelChat ( message . channelId ) ; } ; public MODERATED_CHAT = ( digest , message ) => { this . _database . _updateUser ( digest , message . user ) ; let channel = this . _database . _requireChannel ( message . channelId ) ; if ( channel . appendChat ( message , Models . ChatImportance . Moderated , digest . timestamp ) ) digest . touchChannelChat ( message . channelId ) ; } ; public USER_UPDATE = ( digest , message ) => { this . _database . _updateUser ( digest , message . user ) ; } ; public USER_ADDED = ( digest , message ) => { let channel = this . _database . _requireChannel ( message . channelId ) ; let user = this . _database . _updateUser ( digest , message . user ) ; if ( Utils . setAdd ( channel . users , user . name ) ) digest . touchChannelUsers ( message . channelId ) ; } ; public USER_REMOVED = ( digest , message ) => { let channel = this . _database . _requireChannel ( message . channelId ) ; let user = this . _database . _updateUser ( digest , message . user ) ; if ( Utils . setRemove ( channel . users , user . name ) ) digest . touchChannelUsers ( message . channelId ) ; } ; public GAME_LIST = ( digest , message ) => { if ( message . games ) { for ( let i = 0 ; i < message . games . length ; ++ i ) { let game = message . games [ i ] ; this . _database . _updateGameChannel ( digest , game . channelId , game ) ; } } } ; public GAME_NOTIFY = ( digest , message ) => { this . _database . _updateGameChannel ( digest , message . game . channelId , message . game ) ; if ( digest . notifyChannelId == null ) digest . notifyChannelId = message . game . channelId ; } ; public GAME_CONTAINER_REMOVE_GAME = ( digest , message ) => { let parentChannel = this . _database . _requireChannel ( message . channelId ) as Models . RoomChannel ; if ( ( parentChannel ) && ( parentChannel . removeGame ) && ( parentChannel . removeGame ( message . gameId ) ) ) { digest . touchChannelGames ( parentChannel . channelId ) ; } this . _database . _unjoinChannel ( digest , message . gameId , true ) ; } ; public GAME_STATE = ( digest , message ) => { let gameChannel = this . _database . _requireChannel ( message . channelId , Models . ChannelType . Game ) as Models . GameChannel ; let touchChannel = false ; if ( gameChannel . mergeFlags ( message ) ) touchChannel = true ; if ( gameChannel . doneScoringId != message . doneId ) { gameChannel . doneScoringId = message . doneId ; touchChannel = true ; } if ( touchChannel ) digest . touchChannel ( message . channelId ) ; if ( message . clocks ) { let gameState = this . _database . _createGameState ( digest , message . channelId ) ; gameState . mergeClockStates ( digest . perfstamp , gameChannel . phase , message . clocks . white , message . clocks . black ) ; digest . touchGameClocks ( message . channelId ) ; } let previousGameActions = gameChannel . actions ; gameChannel . clearActions ( ) ; for ( let i = 0 ; i < message . actions . length ; ++ i ) { let user = this . _database . _updateUser ( digest , message . actions [ i ] . user ) ; if ( user . name == this . _database . username ) { gameChannel . enableAction ( message . actions [ i ] . action ) ; } } if ( previousGameActions != gameChannel . actions ) digest . touchGameActions ( message . channelId ) ; } ; public GAME_UPDATE = ( digest , message ) => { if ( ( message . sgfEvents ) && ( message . sgfEvents . length > 0 ) ) { let gameState = this . _database . _createGameState ( digest , message . channelId ) ; gameState . processSGFEvents ( digest . perfstamp , ... message . sgfEvents ) ; digest . touchGameTree ( message . channelId ) ; } } ; public GAME_OVER = ( digest , message ) => { let touchChannel = false ; let gameChannel = this . _database . _requireChannel ( message . channelId , Models . ChannelType . Game ) as Models . GameChannel ; if ( gameChannel . phase != Models . GamePhase . Concluded ) { gameChannel . phase = Models . GamePhase . Concluded ; touchChannel = true ; } if ( gameChannel . mergeScore ( message . score ) ) touchChannel = true ; if ( touchChannel ) digest . touchChannel ( message . channelId ) ; } ; public CHALLENGE_PROPOSAL = ( digest , message ) => { for ( let i = 0 ; i < message . proposal . players . length ; ++ i ) { if ( message . proposal . players [ i ] . user != null ) { this . _database . _updateUser ( digest , message . proposal . players [ i ] . user ) ; } } let gameChannel = this . _database . _createChannel ( digest , message . channelId , Models . ChannelType . Game ) as Models . GameChannel ; if ( gameChannel . mergeProposal ( message . proposal ) ) digest . touchChannel ( gameChannel . channelId ) ; } ; public AUTOMATCH_PREFS = ( digest , message ) => { if ( this . _database . automatch . mergePreferences ( message ) ) digest . automatch = true ; } ; public AUTOMATCH_STATUS = ( digest , message ) => { let seeking = ( message . enabled ) ? true : false ; if ( this . _database . automatch . seeking != seeking ) { this . _database . automatch . seeking = seeking ; digest . automatch = true ; } } ; } } declare var $db ;