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 )