Server.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. const Server = {};
  2. const appConfig = {};
  3. const express = require('express');
  4. const WebSocketServer = require('ws').Server;
  5. const wrtc = require('wrtc');
  6. const Peer = require('simple-peer')
  7. const FPS = 60;
  8. const { SnapshotInterpolation } = require('@geckos.io/snapshot-interpolation');
  9. const SI = new SnapshotInterpolation(FPS)
  10. const path = require('path');
  11. const view = require('view');
  12. let wss;
  13. let peers = {};
  14. Server.listen = async function listenServer ({ root }) {
  15. const app = express();
  16. const server = require('http').Server(app);
  17. app.use(express.static(path.resolve(root + '/../public'), { extensions: ['html'] }));
  18. if (appConfig.cacheView) {
  19. // cached view
  20. console.log('using cached view');
  21. let _view = await viewCreate( { path: root + "/../view" });
  22. app.use(view.middle({view: _view}));
  23. } else {
  24. // uncached view
  25. console.log('using uncached view');
  26. app.use(function(req, res, next){
  27. view.create( { path: root + "/../view" }, function(err, _view){
  28. req.resource = {
  29. params: {}
  30. };
  31. return view.middle({view: _view})(req, res, next)
  32. });
  33. });
  34. }
  35. createWebsocketServer();
  36. server.listen(3000, function () {
  37. console.log(`Listening on ${server.address().port}`);
  38. });
  39. };
  40. // TODO: create new peer for each connected client...
  41. let players = [
  42. 'PLAYER_1',
  43. 'PLAYER_2',
  44. 'PLAYER_3',
  45. 'PLAYER_4'
  46. ];
  47. function createPeer (playerName) {
  48. let peer = new Peer({ wrtc: wrtc });
  49. peer.on('data', function(data){
  50. let Things = require('../../lib/Geoffrey/Things');
  51. //console.log('peer.data', data.toString());
  52. // peer.send('test');
  53. // TODO: add server-side game processing here
  54. // TODO: only rebroadcast game-state, no out of band messages
  55. /*
  56. let msg;
  57. for (let id in peers) {
  58. if (peers[id].writable) {
  59. peers[id].send(data.toString())
  60. }
  61. }
  62. return;
  63. */
  64. try {
  65. msg = JSON.parse(data);
  66. Things[msg.name].inputs = msg.inputs;
  67. // console.log('setting', msg.name, Things[msg.name].inputs)
  68. } catch (err) {
  69. console.log('parsing error', err)
  70. }
  71. });
  72. peer.on('signal', function(data){
  73. console.log('peer.signal', data);
  74. wss.clients.forEach(function each(client) {
  75. console.log('found client', client.id);
  76. client.send(JSON.stringify(data));
  77. });
  78. });
  79. peer.on('close', function () {
  80. console.log("WEBRTC DISCONNECT");
  81. players.unshift(playerName);
  82. });
  83. peer.on('connect', function () {
  84. console.log('connect');
  85. // TODO: random x / y
  86. let startingLocation = {
  87. x: 150 + players.length * 10,
  88. y: 250 + players.length * 10
  89. };
  90. console.log('CREATING NEW PLAYER', playerName)
  91. let p1 = Thing.create({
  92. name: playerName,
  93. x: startingLocation.x,
  94. y: startingLocation.y,
  95. texture: 'mib-caddy'
  96. });
  97. p1.setDepth(10);
  98. Behavior.attach('isMIBCaddy', p1, {
  99. health: 10
  100. });
  101. Behavior.attach('hasScreenWrap', p1);
  102. });
  103. return peer;
  104. }
  105. function createWebsocketServer () {
  106. wss = new WebSocketServer({port: 3434});
  107. console.log('starting websocket server on port 3434');
  108. createHeadlessGame();
  109. wss.on('connection', function(ws) {
  110. // on a websocket connection, get the client ready for a webrtc peering signal with webrtc server
  111. // TODO: uuid?
  112. ws.id = new Date().getTime();
  113. ws.on('close', function(message) {
  114. // console.log("WS DISCONNECT HAPPENED", ws.id)
  115. });
  116. let playerName;
  117. ws.on('message', function(message) {
  118. var data = JSON.parse(message);
  119. console.log('received: %s', message, data);
  120. /*
  121. Perform a handshake here with the client
  122. Now that the client has connected to the game, we need to determine if they will play
  123. Certain settings would be chosen here such as name
  124. */
  125. if (data.type === 'joinGame') {
  126. if (players.length === 0) {
  127. // TODO: send error back to client
  128. console.log('will not allow more than N players. should have stopped before this connected.')
  129. return;
  130. }
  131. // console.log('current available players', players)
  132. playerName = players.shift();
  133. // console.log('accepted game for', playerName)
  134. ws.send(JSON.stringify({ type: 'acceptedGame', playerName: playerName }));
  135. return;
  136. }
  137. if (data.type === 'signal') {
  138. // console.log('sending signal for', playerName);
  139. // this won't work as expected for reconnect, this.id is always new
  140. if (peers[this.id]) {
  141. let peer = peers[this.id];
  142. peer.signal(data.payload)
  143. } else {
  144. let peer = peers[this.id] = createPeer(playerName);
  145. peer.signal(data.payload)
  146. }
  147. }
  148. });
  149. });
  150. }
  151. require('@geckos.io/phaser-on-nodejs')
  152. const Phaser = require('phaser');
  153. const Game = require('../../lib/Geoffrey/Game');
  154. const Thing = require('../../lib/Geoffrey/Thing');
  155. const Behavior = require('../../lib/Geoffrey/Behavior');
  156. Behavior.mode = 'server';
  157. // TODO: add headless server, have it broadcast it's state to each client as the authority
  158. function createHeadlessGame () {
  159. var worldWidth = 1024;
  160. var worldHeight = 768;
  161. // set the fps you need
  162. global.phaserOnNodeFPS = FPS // default is 60
  163. // prepare the config for Phaser
  164. const config = {
  165. type: Phaser.HEADLESS,
  166. width: worldWidth,
  167. height: worldHeight,
  168. banner: false,
  169. audio: false,
  170. fps: {
  171. target: FPS
  172. },
  173. scene: {
  174. preload: Game.preload,
  175. create: Game.create,
  176. update: Game.update,
  177. render: function(){}
  178. },
  179. physics: {
  180. default: 'matter',
  181. matter: {
  182. debug: false, // TODO: use config
  183. gravity: { y: 0, x: 0 },
  184. plugins: {
  185. attractors: true
  186. }
  187. }
  188. }
  189. }
  190. let game = global.game = new Phaser.Game(config);
  191. Game.servermode = true;
  192. Game.bindUpdate(function(game){
  193. const snapshot = SI.snapshot.create(game.gamestate.currentState);
  194. SI.vault.add(snapshot);
  195. for (let id in peers) {
  196. //console.log('state', game.gamestate.currentState);
  197. //console.log('peers[id]', peers[id])
  198. if (peers[id].writable) {
  199. try {
  200. peers[id].send(JSON.stringify(snapshot));
  201. } catch (err) {
  202. }
  203. }
  204. }
  205. return
  206. for (let id in peers) {
  207. //console.log('state', game.gamestate.currentState);
  208. //console.log('ppppp', peers[id])
  209. if (peers[id].writable) {
  210. try {
  211. peers[id].send(JSON.stringify(game.gamestate.currentState));
  212. } catch (err) {
  213. }
  214. }
  215. }
  216. });
  217. Game.bindCreate(function(){
  218. game.peers = peers;
  219. let level0 = Thing.create({
  220. name: 'sector-0',
  221. type: 'server-only'
  222. });
  223. Behavior.attach('isOnlineLevel', level0);
  224. Behavior.attach('hasStateManager', level0);
  225. });
  226. }
  227. module.exports = Server;