/ game-server / source / Server.ts
Server.ts
  1  import { generateErrorFields, setupProcessErrorHandling } from './gameService/ProcessErrors'
  2  
  3  setupProcessErrorHandling()
  4  
  5  import { L2DataLoader } from './gameService/loader/DataLoader'
  6  import { L2GameClientRegistry } from './gameService/L2GameClientRegistry'
  7  import { GameClient } from './gameService/GameClient'
  8  import { LoginServerService } from './loginService/LoginServerService'
  9  import { ConfigManager } from './config/ConfigManager'
 10  import * as net from 'net'
 11  import { AddressInfo, Socket } from 'net'
 12  import { L2World } from './gameService/L2World'
 13  import { EventPoolCache } from './gameService/cache/EventPoolCache'
 14  import perfy from 'perfy'
 15  import { DatabaseManager } from './database/manager'
 16  import { DataManager } from './data/manager'
 17  import aigle from 'aigle'
 18  import { ServerLog, setLogLevel, startExpectedLogging } from './logger/Logger'
 19  import { IPCache, IPCacheConnectionType, IPCacheListType } from './gameService/cache/IPCache'
 20  import chalk from 'chalk'
 21  import { CommandLink } from './rpc/CommandLink'
 22  import { shutdownServices } from './gameService/ShutdownManager'
 23  
 24  let server : net.Server
 25  
 26  function onGameClientJoining( connection: Socket ) : void {
 27      if ( IPCache.isInList( connection, IPCacheConnectionType.GameClient, IPCacheListType.Block ) ) {
 28          connection.end()
 29  
 30          const address = connection.address() as AddressInfo
 31          ServerLog.warn( 'Blocked connection from %s %s', chalk.red( address.address ?? connection.remoteAddress ), address.family ?? connection.remoteFamily )
 32          return
 33      }
 34  
 35      return L2GameClientRegistry.addClient( new GameClient( connection ) )
 36  }
 37  
 38  function onServerStarted() : void {
 39      ServerLog.info( `Game Server accepting L2 H5 clients on ${ ConfigManager.server.getExternalServerIp().join( '.' ) }:${ LoginServerService.getServerPort() }` )
 40  }
 41  
 42  async function shutdownSequence() : Promise<void> {
 43      const players = Object.values( L2GameClientRegistry.playerToClientMap )
 44      if ( players.length > 0 ) {
 45          await aigle.resolve( players ).eachLimit( 5, async ( client : GameClient ) => {
 46              await client.runCleanUpTask()
 47              client.logout()
 48          } )
 49  
 50          ServerLog.info( `Game Shutdown : Removed ${players.length} players` )
 51      }
 52  
 53      server.close()
 54      LoginServerService.closeConnection()
 55      CommandLink.shutdown()
 56  
 57      ServerLog.info( 'Game Shutdown : Server terminated all network connections' )
 58  
 59      await shutdownServices()
 60      ServerLog.info( 'Game Shutdown : services saved all data' )
 61  
 62      DataManager.operations().shutdown()
 63      ServerLog.info( 'Game Shutdown : Datapack performed shutdown' )
 64  
 65      DataManager.getGeoRegionData().shutdown()
 66      ServerLog.info( 'Game Shutdown : Geopack performed shutdown' )
 67  
 68      // TODO : ensure debounced stores are written on shutdown
 69      await DatabaseManager.operations().shutdown()
 70      ServerLog.info( 'Game Shutdown : Database performed shutdown' )
 71  
 72      ServerLog.flush( () => process.exit() )
 73  }
 74  
 75  function onMakeServerAvailable() {
 76      server = net.createServer( onGameClientJoining ).listen( LoginServerService.getServerPort(), onServerStarted )
 77  
 78      ServerLog.info( `Game Server loaded in ${ metrics.time } seconds.` )
 79      ServerLog.info( `Game Server initialized in ${ perfy.end( 'server-start' ).time } seconds.` )
 80      ServerLog.info( `Game Server world contains ${ L2World.getSize() } objects.` )
 81  
 82      LoginServerService.enableGameServer()
 83  
 84      if ( global.gc ) {
 85          global.gc()
 86      }
 87  
 88      /*
 89          Log level is now set to correct configuration level once
 90          we are done with loading of initial data.
 91       */
 92      startExpectedLogging()
 93      generateErrorFields()
 94  
 95      EventPoolCache.setLogging( true )
 96  
 97      /*
 98          We must be able to load all data and initialize server before we are able to shut down
 99          all parts properly.
100       */
101      /*
102          Used by various restart utilities that manage application process.
103       */
104      process.once( 'SIGUSR1', shutdownSequence )
105      process.once( 'SIGUSR2', shutdownSequence )
106  
107      /*
108          Used by terminal applications to interrupt process, aka Ctrl + C.
109       */
110      process.on( 'SIGINT', shutdownSequence )
111  
112      /*
113          Termination signal used by OS to signify process must be killed (similar to SIGKILL).
114       */
115      process.on( 'SIGTERM', shutdownSequence )
116  }
117  
118  let metrics = perfy.end( 'server-load' )
119  perfy.start( 'server-start' )
120  
121  setLogLevel( 'info' )
122  
123  LoginServerService.connectToLoginServer()
124  L2DataLoader.start().then( onMakeServerAvailable )