var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); /** Config-Values NOTE EDIT HERE **/ var Configuration = { cube_width : 50, cube_height : 200, cube_speed : 7, ball_size : 15, ball_speed : 15, ball_owner : Math.round(Math.random()) + 1, ball_maxAngle : 5*Math.PI/12, wincount : 3 } /** Parameters **/ var clients = [], // List of all conected clients players = [], // Store Player 1 and Player 2 in Array {Player1 == players[0]} gameState = 0, // Game-Controler Variable width, height, // Final Width + Height of Game-Window startinterval, // Interval for Counting down (cancelable) calcPos, score = [0,0], id = 0; /** Game-Config **/ var cube_width = Configuration.cube_width, cube_height = Configuration.cube_height, cube_speed = Configuration.cube_speed, // Depending on 'tickrate' [cube_speed*tickrate == acutal Speed] wincount = Configuration.wincount; /** Networking-Config **/ var tickrate = 20; /** Ball-Config **/ var ball_size = Configuration.ball_size, ball_speed = Configuration.ball_speed, ball_owner = Configuration.ball_owner, ball_maxAngle = Configuration.ball_maxAngle, ball_VX = 0, ball_VY = 0, ballX, ballY; /** ########## **/ /** CONNECTION **/ /** ########## **/ io.on('connection', function(socket){ /** Set ID for User **/ id++; var clientkey = clients.push(new Client(id, socket)) - 1; console.log('Client connected. ID=' + id); /** ######### **/ /** HANDSHAKE **/ /** ######### **/ socket.emit('handshake', id, gameState, cube_width, cube_height, cube_speed, score, function(w, h){ // Callback function for Client to set screen size // clients[clientkey].width = w; clients[clientkey].height = h; var pid = 0; if(players.length < 2) pid = players.push(new Player(id, socket, w, h)); socket.emit('setpid', pid); if(players.length > 1 && gameState == 0) setUp(); }); /** If game was already started send Window-Detials **/ if(gameState > 0) socket.emit('size', width, height); /** #### **/ /** Ping **/ /** ### **/ socket.on('appping', function(fn){ fn(Date.now()); }); /** ######### **/ /** Ballshoot **/ /** ######### **/ socket.on('ballshoot', function(y){ console.log('### Ball shoot event ###'); if(ball_owner == 1){ ball_VX = ball_speed; console.log('owner: left - at:' + players[0].y); } else if(ball_owner == 2){ ball_VX = -ball_speed; console.log('owner: right - at:' + players[1].y); } ball_owner = 0; }); /** ################ **/ /** Paddel V-Change **/ /** ############## **/ socket.on('vchange', function(pos, val, y, timestamp){ try{ players[pos-1].vY = val; } catch (err) { console.log('Error in vChange: ' + err.message); } // NOTE Interpolation not needed // console.log('INTERPOLATING...'); // var diff = ((players[pos-1].speed/tickrate)*timediff); // if(players[pos-1].vY == 0){ // Lag from Sender (accel) // if(val > 0) // players[pos-1].y -= diff; // if(val < 0) // players[pos-1].y += diff; // } // if(players[pos-1].vY == 1){ // if(val == 0) // players[pos-1].y += diff; // } // if(players[pos-1].vY == -1){ // if(val == 0) // players[pos-1].y -= diff; // } // console.log('Diff:' + diff); // console.log('Got: ' + y + ' @ ' + timestamp); // console.log('Expected:' + players[pos-1].y + ' @ ' + Date.now()); // socket.emit('changeV', pos, val, players[pos-1].y); // socket.broadcast.emit('changeV', pos, val, players[pos-1].y); }); /** ########## **/ /** DISCONNECT **/ /** ########## **/ socket.on('disconnect', function(){ var cid, key; // Client ID clients.forEach(function(cl, k){ if(cl.socket == socket){ cid = cl.id; key = k; return; } }); var pid = 0; // 0 == Spectator --- 1 == Player 1 ---- 2 == Player 2 players.forEach(function(pl, kkey){ if(pl.socket == socket){ pid = kkey+1; return; } }); console.log('Client disconnected. ID=' + cid); clients.splice(key, 1); if(clients.length == 0){ console.log('All Clients disonnected. Reseting...'); resetServer(); return; } if(pid == 0) return; // Don't do anything if spectator leaves gameState = 3; broadcastGameState(); if(pid == 1){ clients[0].socket.emit('win', 2, 'Player 1 left.'); clients[0].socket.broadcast.emit('win', 2, 'Player 1 left.'); } if(pid == 2){ clients[0].socket.emit('win', 1, 'Player 2 left.'); clients[0].socket.broadcast.emit('win', 1, 'Player 2 left.'); } resetServer(); console.log('Reseting...'); }); }); http.listen(3000, function(){ console.log('listening on *:3000'); }); /** Sync all settings to client and start the game **/ function setUp(){ gameState = 1; broadcastGameState(); console.log('Starting SetUp...'); /** Select smalest screensize and set for all clients **/ if(players[0].window_width < players[1].window_width) width = players[0].window_width; else width = players[1].window_width; if(players[0].window_height < players[1].window_height) height = players[0].window_height; else height = players[1].window_height; if(calcPos == null) // Start tick Server only once tickServer(); clients[0].socket.emit('size', width, height); clients[0].socket.broadcast.emit('size', width, height); var i = 5; startinterval = setInterval(function(){ if(gameState == 3) clearInterval(startinterval); i--; clients[0].socket.emit('countdown', i); clients[0].socket.broadcast.emit('countdown', i); if(i == 0){ resetPlayers(); clearInterval(startinterval); gameState = 2; broadcastGameState(); } }, 1000); } /** Broadcast GameState to all clients **/ function broadcastGameState(){ clients[0].socket.emit('gamestate', gameState); clients[0].socket.broadcast.emit('gamestate', gameState); } function tickServer(){ calcPos = setInterval(function(){ try { calculatePlayerPosition(); calculateBallPosition(); } catch (err){ console.log('Error in ServerTick: ' + err.message); } }, tickrate); } function calculatePlayerPosition(){ players.forEach(function(player, k){ player.y -= player.vY*player.speed; if(player.y > height-player.height || player.y < 0) player.vY = 0; if(player.y < 0) player.y = 0; if(player.y > height-player.height) player.y = height-player.height; player.socket.emit('paddlepos', k+1, player.vY, player.y); player.socket.broadcast.emit('paddlepos', k+1, player.vY, player.y); }); } function calculateBallPosition(){ /** Move Ball with Paddle if owned by owner (@roundstart) **/ if(ball_owner == 1){ ballX = cube_width+ball_size, ballY = players[0].y+cube_height/2; } else if(ball_owner == 2){ ballX = width-cube_width-ball_size, ballY = players[1].y+cube_height/2; } /** ############ **/ /** Ball Physics **/ /** ############ **/ ballX += ball_VX; ballY += ball_VY; /** Collide with right Paddle or get point and respawn **/ if(ballX > width - cube_width){ if(ballY < players[1].y || ballY > players[1].y+cube_height){ console.log('Point for Left'); changeScore(1, score[0]+1); ball_VX = 0; ball_VY = 0; ball_owner = 1; } else { var intersect = ((players[1].y+cube_height/2)-ballY)/(cube_height/2); console.log('Intersect with right at:'); console.log(intersect); console.log('###########################'); ball_VX = -ball_speed*Math.cos(intersect*ball_maxAngle); ball_VY = -ball_speed*Math.sin(intersect*ball_maxAngle); } } /** Collide with left Paddle or get point and respawn **/ if(ballX < cube_width){ if(ballY < players[0].y || ballY > players[0].y+cube_height){ console.log('Point for Right'); changeScore(2, score[1]+1); ball_VX = 0; ball_VY = 0; ball_owner = 2; } else { var intersect = ((players[0].y+cube_height/2)-ballY)/(cube_height/2); console.log('Intersect with left at:'); console.log(intersect); console.log('###########################'); ball_VX = ball_speed*Math.cos(intersect*ball_maxAngle); ball_VY = ball_speed*Math.sin(intersect*ball_maxAngle); } } /** Collide with walls **/ if(ballY <= 0 || ballY >= height-ball_size){ ball_VY = -ball_VY; console.log(ballX + ' : ' + ballY); } var data = { x : ballX, y : ballY, vx : ball_VX, vy : ball_VY, speed : ball_speed, owner : ball_owner }; players[0].socket.emit('ballpos', data); players[0].socket.broadcast.emit('ballpos', data); } function changeScore(id, val){ score[id-1] = val; clients[0].socket.emit('score', score); clients[0].socket.broadcast.emit('score', score); if(val == wincount){ gameState = 3; broadcastGameState(); clients[0].socket.emit('win', id, 'Player ' + id + ' scored needed points first.'); clients[0].socket.broadcast.emit('win', id, 'Player ' + id + ' scored needed points first.'); resetServer(); } } function resetPlayers(){ ball_owner = Math.round(Math.random()) + 1; ball_VX = 0; ball_VY = 0; players.forEach(function(player, k){ player.speed = cube_speed; player.width = cube_width; player.height = cube_height; player.y = 0; player.vY = 0; changeScore(k, 0); }); } function resetServer(){ gameState = 0; clearInterval(calcPos); calcPos = null; clearInterval(startinterval); score = [0,0]; cube_width = Configuration.cube_width; cube_height = Configuration.cube_height; cube_speed = Configuration.cube_speed; ball_size = Configuration.ball_size; ball_speed = Configuration.ball_speed; ball_owner = Configuration.ball_owner; ball_maxAngle = Configuration.ball_maxAngle; ball_VX = 0; ball_VY = 0; ballX = Configuration.cube_width; ballY = Configuration.cube_height/2; players = []; } function Client(id, socket){ this.id = id; this.socket = socket; this.width = 0; this.height = 0; } function Player(id, socket, window_width, window_height){ this.id = id; this.socket = socket; this.window_width = window_width; this.window_height = window_height; this.speed = cube_speed; this.width = cube_width; this.height = cube_height; this.y = 0; this.vY = 0; }