lobby.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. require("./statistics.js");
  2. require("./view_stats.js");
  3. const { count } = require('console');
  4. const express = require('express');
  5. const https=require('https')
  6. const app = express();
  7. const port = 1337;
  8. const fs = require('fs');
  9. const path=require('path');
  10. var os = require('os');
  11. stats_start();
  12. view_stats( app );
  13. const args = process.argv;
  14. console.log("Args:");
  15. console.log(args);
  16. console.log( "local? = " + ( args['--local'] ) );
  17. // constants -------------------------------
  18. const CLEANUP_INTERVAL = 1000 * 1 ; // 1 seconds
  19. const LOBBY_TIMEOUT_THRESHOLD = 1000 * 60 * 10 ; // 10 minutes
  20. const PLAYER_TIMEOUT_THRESHOLD = 1000 * 1.5 ; // 1.5 seconds
  21. const STATS_RESET_TIMER = 1000 * 60; // 1 minutes
  22. // intervals -----------------------------------
  23. setInterval( cleanup_stats , STATS_RESET_TIMER );
  24. function cleanup_stats()
  25. {
  26. let d = (new Date());
  27. let file_date = d.getYear() + "_" + d.getMonth() + "_" + d.getDate() ;
  28. let csv_file = `./log/stats_${file_date}.csv`;
  29. // append data
  30. stats_data[ 'activeLobbies' ] = lobbies.filter( x => x.isActive ).length;
  31. // csv output
  32. csv_header = Object.keys( stats_data );
  33. csv_line = Object.values( stats_data ).toString();
  34. // handle file
  35. fs.stat( csv_file , function( err, file_stat ) {
  36. if (err == null) {
  37. // console.log('File exists');
  38. } else if (err.code === 'ENOENT') {
  39. // file does not exist
  40. fs.writeFileSync( csv_file , csv_header + os.EOL );
  41. } else console.log('Error: ', err.code );
  42. fs.open( csv_file , 'a', 666 , function( e, id ) {
  43. fs.write( id, csv_line + os.EOL, null, 'utf8', function(){
  44. fs.close(id, function(){
  45. // console.log('file is updated');
  46. });
  47. });
  48. });
  49. // reset stats
  50. stats_reset();
  51. });
  52. }
  53. setInterval( cleanup, CLEANUP_INTERVAL);
  54. // app configuration -----------------------
  55. app.use(express.json()); // Middleware to parse JSON bodies
  56. if( args.indexOf('--local') != -1)
  57. {
  58. app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); });
  59. }
  60. else
  61. {
  62. const options = {
  63. key:fs.readFileSync(path.join(__dirname,'../cert/key.pem')),
  64. cert:fs.readFileSync(path.join(__dirname,'../cert/cert.pem'))
  65. }
  66. const sslServer=https.createServer(options,app);
  67. sslServer.listen(port,()=>{ console.log( 'Secure server is listening on port ' + port ); })
  68. }
  69. process.on('exit', () => {
  70. writeAppLog();
  71. });
  72. process.on('SIGINT', () => {
  73. writeAppLog();
  74. process.exit();
  75. });
  76. function cleanup()
  77. {
  78. const now = Date.now();
  79. lobbies.forEach( _L => {
  80. let host_left = false;
  81. let active_player_count = 0;
  82. _L.players.forEach( _P => {
  83. _P.isActive = ( now - _P.lastActive ) < PLAYER_TIMEOUT_THRESHOLD ;
  84. if( _P.isActive ) active_player_count ++ ;
  85. if( ! _P.isActive && _P.playerId == _L.hostId )
  86. {
  87. host_left = true;
  88. }
  89. } );
  90. _L.isActive = active_player_count > 0 ;
  91. if( host_left )
  92. {
  93. let active_players = _L.players.filter( x => x.isActive );
  94. if( active_players.length < 1 )
  95. {
  96. // lobby is empty
  97. }
  98. else
  99. {
  100. _L.joinCode = ""; // clean prev join code
  101. _L.hostMigration = true;
  102. _L.hostId = active_players[ 0 ].playerId;
  103. }
  104. }
  105. } );
  106. lobbies = lobbies.filter(x => ( ( now - x.lastActive ) < LOBBY_TIMEOUT_THRESHOLD ) );
  107. writeAppLog();
  108. }
  109. // app log -----------------------------------
  110. function writeAppLog()
  111. {
  112. let out_text = "";
  113. out_text += `"stats":${JSON.stringify(stats_data,null,3)} , \n`;
  114. const activeLobbies = lobbies.filter( x => x.isActive );
  115. out_text += `"lobbies":${JSON.stringify(activeLobbies,null,4)}\n`;
  116. out_text = `{\n${out_text}}`;
  117. fs.writeFileSync('./log/applog.log', out_text );
  118. }
  119. app.get('/viewLog', (req, res) => {
  120. stats_RecordRequest(req);
  121. fs.readFile('./log/applog.log', 'utf8', (err, data) => {
  122. if (err) {
  123. console.error("Error reading ./log/applog.log:", err);
  124. res.status(500).send('Error reading log file');
  125. return;
  126. }
  127. res.type('text/plain');
  128. res.send(data);
  129. });
  130. });
  131. app.get('/viewStats', (req, res ) => {
  132. stats_RecordRequest(req);
  133. res.send(stats_data);
  134. });
  135. // players & lobby ------------------------------
  136. let lobbies = [];
  137. let lobby_index = lobbyKey => lobbies.findIndex( x => x.lobbyKey === lobbyKey );
  138. function lobby_new( lobbyKey , playerId , lobbyName ) {
  139. return {
  140. lobbyKey ,
  141. lobbyName ,
  142. isActive : true ,
  143. hostId : playerId ,
  144. hostMigration : false ,
  145. joinCode : "" ,
  146. region : "",
  147. createdAt : Date.now(),
  148. lastActive : Date.now(),
  149. players : [ player_new( playerId ) ]
  150. };
  151. }
  152. function player_new( playerId ) {
  153. return {
  154. playerId ,
  155. lastActive: Date.now() ,
  156. isActive : true
  157. };
  158. }
  159. app.post("/lobbies" , (req, res) => {
  160. stats_RecordRequest(req);
  161. let now = Date.now();
  162. let active_lobbies = lobbies.filter( _L => {
  163. // filter ( optional )
  164. for( let k in req.body )
  165. if( _L[ k ] !== req.body[ k ] )
  166. return false;
  167. let active_players = _L.players.filter( x => x.isActive );
  168. return _L.isActive && active_players.length > 0;
  169. } );
  170. active_lobbies = active_lobbies.map( _L => ( {
  171. lobbyKey : _L.lobbyKey ,
  172. lobbyName : _L.lobbyName ,
  173. joinCode : _L.joinCode ,
  174. aliveDuration : ( now - _L.createdAt ),
  175. playerCount : _L.players.filter( x => x.isActive ).length
  176. } ) );
  177. res.send( { count : active_lobbies.length , data : active_lobbies } );
  178. });
  179. app.post("/lobby" , (req, res ) =>
  180. {
  181. stats_RecordRequest(req);
  182. output = { found : false };
  183. const { lobbyKey } = req.body;
  184. let index = lobby_index( lobbyKey );
  185. if( index >= 0 )
  186. {
  187. output.found = true;
  188. output.data = lobbies[ index ];
  189. }
  190. res.send( output );
  191. } );
  192. app.post('/lobbySet', ( req, res ) =>
  193. {
  194. stats_RecordRequest(req);
  195. output = { found : false };
  196. const { lobbyKey } = req.body;
  197. let index = lobby_index( lobbyKey );
  198. if( index >= 0 )
  199. {
  200. let lobby = lobbies[ index ];
  201. for( let k in req.body )
  202. if( k !== "lobbyKey" )
  203. lobby[ k ] = req.body[ k ];
  204. output.found = true;
  205. output.data = lobbies[ index ];
  206. }
  207. res.send( output );
  208. } );
  209. app.post('/lobbyJoin', ( req, res ) =>
  210. {
  211. stats_RecordRequest(req);
  212. const { lobbyKey , playerId , lobbyName } = req.body;
  213. let output = { created : false, joined : false, alreadyJoined : false };
  214. let index = lobby_index( lobbyKey );
  215. if( index< 0 )
  216. {
  217. let lobby = lobby_new( lobbyKey , playerId , lobbyName );
  218. lobbies.push( lobby );
  219. output.created = true;
  220. }
  221. else
  222. {
  223. let lobby = lobbies[ index ];
  224. lobby.lastActive = Date.now();
  225. let player_index = lobby.players.findIndex( x => x.playerId == playerId );
  226. if( player_index < 0 )
  227. {
  228. lobby.players.push( player_new ( playerId ) );
  229. output.joined = true;
  230. }
  231. else
  232. {
  233. let player = lobby.players[ player_index ];
  234. player.lastActive = Date.now();
  235. lobby.players[ player_index ] = player;
  236. output.alreadyJoined = true;
  237. }
  238. lobbies[ index ] = lobby;
  239. }
  240. res.send( output );
  241. });