440 lines
9.7 KiB
Plaintext
440 lines
9.7 KiB
Plaintext
|
#!/usr/bin/env node
|
||
|
|
||
|
/**
|
||
|
* ping
|
||
|
* https://github.com/chjj/blessed
|
||
|
* Copyright (c) 2013, Christopher Jeffrey (MIT License)
|
||
|
* Online (ping)pong in your terminal.
|
||
|
*/
|
||
|
|
||
|
// Example Usage:
|
||
|
// Server: $ ./example/ping 3000
|
||
|
// Client: $ ./example/ping 127.0.0.1 3000
|
||
|
// Demo: $ ./example/ping
|
||
|
|
||
|
process.title = 'ping';
|
||
|
|
||
|
if (/^(-h|--help|-\?)$/.test(process.argv[2])) {
|
||
|
console.log('node-ping');
|
||
|
console.log('Example Usage:');
|
||
|
console.log('Server: $ node-ping 3000');
|
||
|
console.log('Client: $ node-ping 127.0.0.1 3000');
|
||
|
console.log('Demo: $ node-ping');
|
||
|
return process.exit(0);
|
||
|
}
|
||
|
|
||
|
var blessed = require('blessed')
|
||
|
, program = blessed()
|
||
|
, nssocket;
|
||
|
|
||
|
try {
|
||
|
nssocket = require('nssocket');
|
||
|
} catch (e) {
|
||
|
;
|
||
|
}
|
||
|
|
||
|
var server
|
||
|
, socket;
|
||
|
|
||
|
/**
|
||
|
* Screen Layout
|
||
|
*/
|
||
|
|
||
|
var screen = new blessed.Screen({
|
||
|
program: program
|
||
|
});
|
||
|
|
||
|
var table = new blessed.Box({
|
||
|
left: 0,
|
||
|
top: 0,
|
||
|
width: screen.width,
|
||
|
height: screen.height
|
||
|
});
|
||
|
|
||
|
var ball = new blessed.Box({
|
||
|
width: 1,
|
||
|
height: 1,
|
||
|
bg: 'white',
|
||
|
top: 0,
|
||
|
left: 0
|
||
|
});
|
||
|
|
||
|
var lpaddle = new blessed.Box({
|
||
|
width: 1,
|
||
|
height: 3,
|
||
|
bg: 'yellow',
|
||
|
top: 0,
|
||
|
left: 0
|
||
|
});
|
||
|
|
||
|
var rpaddle = new blessed.Box({
|
||
|
width: 1,
|
||
|
height: 3,
|
||
|
bg: 'yellow',
|
||
|
top: 0,
|
||
|
right: 0
|
||
|
});
|
||
|
|
||
|
var score = new blessed.Box({
|
||
|
top: 0,
|
||
|
left: 4,
|
||
|
height: 3,
|
||
|
width: 20,
|
||
|
bold: true
|
||
|
});
|
||
|
|
||
|
score.lwins = 0;
|
||
|
score.rwins = 0;
|
||
|
|
||
|
var net = new blessed.Box({
|
||
|
width: 1,
|
||
|
height: '100%',
|
||
|
bg: 'yellow',
|
||
|
top: 0,
|
||
|
left: 'center'
|
||
|
});
|
||
|
|
||
|
var message = new blessed.Box({
|
||
|
width: '50%',
|
||
|
height: 3,
|
||
|
border: {
|
||
|
type: 'ascii'
|
||
|
},
|
||
|
top: 'center',
|
||
|
left: 'center'
|
||
|
});
|
||
|
|
||
|
var text = new blessed.Box({
|
||
|
top: 'center',
|
||
|
left: 1,
|
||
|
right: 1,
|
||
|
height: 1,
|
||
|
content: 'Waiting for players to connect...'
|
||
|
});
|
||
|
|
||
|
message.append(text);
|
||
|
|
||
|
screen.append(table);
|
||
|
|
||
|
table.append(score);
|
||
|
table.append(lpaddle);
|
||
|
table.append(rpaddle);
|
||
|
table.append(net);
|
||
|
table.append(ball);
|
||
|
table.append(message);
|
||
|
|
||
|
screen.on('resize', function() {
|
||
|
table.width = screen.width;
|
||
|
table.height = screen.height;
|
||
|
[ball, lpaddle, rpaddle].forEach(function(el) {
|
||
|
if (el.rbottom < 0) el.rtop = table.height - 1 - el.height;
|
||
|
if (el.rright < 0) el.rleft = table.width - 1;
|
||
|
});
|
||
|
screen.render();
|
||
|
sync();
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Options
|
||
|
*/
|
||
|
|
||
|
ball.speed = 2;
|
||
|
ball.unpredictable = true;
|
||
|
lpaddle.speed = 2;
|
||
|
rpaddle.speed = 2;
|
||
|
|
||
|
/**
|
||
|
* Game
|
||
|
*/
|
||
|
|
||
|
function sync() {
|
||
|
if (!socket) return;
|
||
|
socket.send(['update'], {
|
||
|
table: { width: table.width, height: table.height },
|
||
|
lpaddle: { rleft: lpaddle.rleft, rtop: lpaddle.rtop, speed: lpaddle.speed },
|
||
|
rpaddle: { rleft: rpaddle.rleft, rtop: rpaddle.rtop, speed: rpaddle.speed },
|
||
|
score: { lwins: score.lwins, rwins: score.rwins }
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function reset() {
|
||
|
text.setContent('Waiting for players to connect...');
|
||
|
message.hide();
|
||
|
ball.moving = true;
|
||
|
ball.direction = 'right';
|
||
|
ball.angle = 'down';
|
||
|
ball.rtop = 1;
|
||
|
ball.rleft = 1;
|
||
|
lpaddle.rtop = 0;
|
||
|
rpaddle.rtop = 0;
|
||
|
|
||
|
score.setContent('Score: ' + score.lwins + ' | ' + score.rwins);
|
||
|
|
||
|
rpaddle.movable = true;
|
||
|
|
||
|
screen.render();
|
||
|
if (server && socket) {
|
||
|
socket.send(['reset']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function rand(min, max) {
|
||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||
|
}
|
||
|
|
||
|
function startGame() {
|
||
|
reset();
|
||
|
|
||
|
if (startGame._bound) return;
|
||
|
startGame._bound = true;
|
||
|
|
||
|
screen.on('keypress', function(ch, key) {
|
||
|
if (!ball.moving) return;
|
||
|
if (key.name === 'up' || key.name === 'k') {
|
||
|
if (socket) socket.send(['up']);
|
||
|
if (lpaddle.rtop > 0) lpaddle.rtop -= lpaddle.speed;
|
||
|
if (!socket) if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed;
|
||
|
if (lpaddle.rtop < 0) lpaddle.rtop = 0;
|
||
|
if (rpaddle.rtop < 0) rpaddle.rtop = 0;
|
||
|
screen.render();
|
||
|
} else if (key.name === 'down' || key.name === 'j') {
|
||
|
if (socket) socket.send(['down']);
|
||
|
if (lpaddle.rbottom > 0) lpaddle.rtop += lpaddle.speed;
|
||
|
if (!socket) if (rpaddle.rbottom > 0) rpaddle.rtop += rpaddle.speed;
|
||
|
if (lpaddle.rbottom < 0) lpaddle.rtop = table.height - lpaddle.height - 1;
|
||
|
if (rpaddle.rbottom < 0) rpaddle.rbottom = table.height - rpaddle.height - 1;
|
||
|
screen.render();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
setInterval(function() {
|
||
|
if (!ball.moving) return;
|
||
|
if (ball.direction === 'right') {
|
||
|
if (ball.rright > 1) {
|
||
|
ball.rleft += ball.speed;
|
||
|
} else {
|
||
|
if (ball.rtop >= rpaddle.rtop && ball.rtop <= rpaddle.rtop + rpaddle.height) {
|
||
|
ball.direction = 'left';
|
||
|
ball.rleft -= ball.speed;
|
||
|
|
||
|
ball.rleft -= rand(0, 3);
|
||
|
if (ball.angle === 'down') ball.rtop += rand(0, 3);
|
||
|
else if (ball.angle === 'up') ball.rtop -= rand(0, 3);
|
||
|
} else {
|
||
|
// Right loses
|
||
|
score.lwins++;
|
||
|
ball.rleft = table.width - 1;
|
||
|
if (socket) socket.send(['lose']);
|
||
|
ball.moving = false;
|
||
|
text.setContent('Right player loses!');
|
||
|
message.show();
|
||
|
setTimeout(reset, 3000);
|
||
|
screen.render();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (ball.rright < 1) ball.rleft = table.width - 2;
|
||
|
} else if (ball.direction === 'left') {
|
||
|
if (ball.rleft > 1) {
|
||
|
ball.rleft -= ball.speed;
|
||
|
} else {
|
||
|
if (ball.rtop >= lpaddle.rtop && ball.rtop <= lpaddle.rtop + lpaddle.height) {
|
||
|
ball.direction = 'right';
|
||
|
ball.rleft += ball.speed;
|
||
|
|
||
|
ball.rleft += rand(0, 3);
|
||
|
if (ball.angle === 'down') ball.rtop += rand(0, 3);
|
||
|
else if (ball.angle === 'up') ball.rtop -= rand(0, 3);
|
||
|
} else {
|
||
|
// Left loses
|
||
|
score.rwins++;
|
||
|
ball.rleft = 0;
|
||
|
if (socket) socket.send(['win']);
|
||
|
ball.moving = false;
|
||
|
text.setContent('Left player loses!');
|
||
|
message.show();
|
||
|
setTimeout(reset, 3000);
|
||
|
screen.render();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (ball.rleft < 1) ball.rleft = 1;
|
||
|
}
|
||
|
if (ball.angle === 'down') {
|
||
|
if (ball.rbottom > 0) {
|
||
|
ball.rtop++;
|
||
|
if (ball.unpredictable) ball.rtop += rand(0, 3);
|
||
|
} else {
|
||
|
ball.angle = 'up';
|
||
|
ball.rtop--;
|
||
|
}
|
||
|
} else if (ball.angle === 'up') {
|
||
|
if (ball.rtop > 0) {
|
||
|
ball.rtop--;
|
||
|
if (ball.unpredictable) ball.rtop -= rand(0, 3);
|
||
|
} else {
|
||
|
ball.angle = 'down';
|
||
|
ball.rtop++;
|
||
|
}
|
||
|
}
|
||
|
if (ball.rtop < 0) ball.rtop = 0;
|
||
|
if (ball.rbottom < 0) ball.rtop = table.height - 1;
|
||
|
if (socket) socket.send(['ball'], { rleft: ball.rleft, rtop: ball.rtop });
|
||
|
screen.render();
|
||
|
}, 100);
|
||
|
}
|
||
|
|
||
|
function startServer() {
|
||
|
server = nssocket.createServer({}, function(socket_) {
|
||
|
socket = socket_;
|
||
|
|
||
|
sync();
|
||
|
|
||
|
socket.data(['up'], function() {
|
||
|
if (!ball.moving) return;
|
||
|
if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed;
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['down'], function() {
|
||
|
if (!ball.moving) return;
|
||
|
if (rpaddle.rtop < table.height - 1) rpaddle.rtop += rpaddle.speed;
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.on('error', function() {
|
||
|
socket = null;
|
||
|
reset();
|
||
|
ball.moving = false;
|
||
|
message.show();
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
startGame();
|
||
|
});
|
||
|
|
||
|
server.listen(+process.argv[2]);
|
||
|
}
|
||
|
|
||
|
function startClient() {
|
||
|
var socket = new nssocket.NsSocket({
|
||
|
reconnect: true,
|
||
|
maxRetries: Infinity,
|
||
|
retryInterval: 5000
|
||
|
});
|
||
|
|
||
|
socket.connect(+process.argv[3], process.argv[2]);
|
||
|
|
||
|
screen.on('keypress', function(ch, key) {
|
||
|
if (!rpaddle.movable) return;
|
||
|
if (key.name === 'up' || key.name === 'k') {
|
||
|
socket.send(['up']);
|
||
|
if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed;
|
||
|
if (rpaddle.rtop < 0) rpaddle.rtop = 0;
|
||
|
screen.render();
|
||
|
} else if (key.name === 'down' || key.name === 'j') {
|
||
|
socket.send(['down']);
|
||
|
if (rpaddle.rbottom > 0) rpaddle.rtop += rpaddle.speed;
|
||
|
if (rpaddle.rbottom < 0) rpaddle.rtop = table.height - rpaddle.height - 1;
|
||
|
screen.render();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
socket.data(['up'], function() {
|
||
|
if (lpaddle.rtop > 0) lpaddle.rtop -= lpaddle.speed;
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['down'], function() {
|
||
|
if (lpaddle.rtop < table.height - 1) lpaddle.rtop += lpaddle.speed;
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['ball'], function(data) {
|
||
|
ball.rleft = data.rleft;
|
||
|
ball.rtop = data.rtop;
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['update'], function(data) {
|
||
|
if (data.lpaddle) {
|
||
|
lpaddle.rleft = data.lpaddle.rleft;
|
||
|
lpaddle.rtop = data.lpaddle.rtop;
|
||
|
lpaddle.speed = data.lpaddle.speed;
|
||
|
}
|
||
|
|
||
|
if (data.rpaddle) {
|
||
|
rpaddle.rleft = data.rpaddle.rleft;
|
||
|
rpaddle.rtop = data.rpaddle.rtop;
|
||
|
rpaddle.speed = data.rpaddle.speed;
|
||
|
}
|
||
|
|
||
|
if (data.ball) {
|
||
|
ball.moving = data.ball.moving;
|
||
|
ball.rleft = data.ball.rleft;
|
||
|
ball.rtop = data.ball.rtop;
|
||
|
}
|
||
|
|
||
|
if (data.table) {
|
||
|
table.height = data.table.height;
|
||
|
table.width = data.table.width;
|
||
|
}
|
||
|
|
||
|
if (data.score) {
|
||
|
score.lwins = data.score.lwins;
|
||
|
score.rwins = data.score.rwins;
|
||
|
}
|
||
|
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['win'], function() {
|
||
|
rpaddle.movable = false;
|
||
|
score.rwins++;
|
||
|
text.setContent('Left player loses!');
|
||
|
message.show();
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['lose'], function() {
|
||
|
rpaddle.movable = false;
|
||
|
score.lwins++;
|
||
|
text.setContent('Right player loses!');
|
||
|
message.show();
|
||
|
screen.render();
|
||
|
});
|
||
|
|
||
|
socket.data(['reset'], reset);
|
||
|
|
||
|
reset();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Main
|
||
|
*/
|
||
|
|
||
|
function main() {
|
||
|
screen.on('keypress', function(ch, key) {
|
||
|
if (key.name === 'q' || key.name === 'escape') {
|
||
|
return process.exit(0);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
screen.render();
|
||
|
|
||
|
// Demo Mode / Single Player
|
||
|
if (!nssocket || !process.argv[2]) return startGame();
|
||
|
|
||
|
// Server Mode
|
||
|
if (!process.argv[3]) return startServer();
|
||
|
|
||
|
// Client Mode
|
||
|
if (process.argv[2] && process.argv[3]) return startClient();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Execute
|
||
|
*/
|
||
|
|
||
|
main();
|