Server and client code completed (with bugs).

This commit is contained in:
theKidOfArcrania 2017-02-26 02:36:44 +00:00
parent 916c50e162
commit 88a6195fe8
10 changed files with 8383 additions and 283 deletions

View File

@ -3,6 +3,11 @@ var Player = require("./player.js");
var renderer = require("./game-renderer.js"); var renderer = require("./game-renderer.js");
var consts = require("./game-consts.js"); var consts = require("./game-consts.js");
var GRID_SIZE = consts.GRID_SIZE;
var CELL_WIDTH = consts.CELL_WIDTH;
renderer.allowAnimation = true;
/** /**
* Provides requestAnimationFrame in a cross browser way. * Provides requestAnimationFrame in a cross browser way.
* @author paulirish / http://paulirish.com/ * @author paulirish / http://paulirish.com/
@ -22,84 +27,10 @@ if ( !window.requestAnimationFrame ) {
})(); })();
} }
var user, socket, frame;
$(function() { //Event listeners
var GRID_SIZE = consts.GRID_SIZE; $(document).keydown(function(e) {
var CELL_WIDTH = consts.CELL_WIDTH;
var canvas = $("#main-ui")[0];
var ctx2 = canvas.getContext('2d');
var grid = renderer.grid;
renderer.allowAnimation = true;
//Load players.
for (var p = 0; p < 9; p++)
{
//TODO: socket loading.
var pRow = getRandomInt(0, GRID_SIZE);
var pCol = getRandomInt(0, GRID_SIZE);
var sdata = {
posX: pCol * CELL_WIDTH,
posY: pRow * CELL_WIDTH,
currentHeading: getRandomInt(0, 4),
//name: ...,
num: p
};
renderer.addPlayer(new Player(true, grid, sdata));
for (var dr = -1; dr <= 1; dr++)
for (var dc = -1; dc <= 1; dc++)
if (!grid.isOutOfBounds(dr + pRow, dc + pCol))
grid.set(dr + pRow, dc + pCol, renderer.getPlayer(p));
}
//Load grid.
for (var r = 0; r < grid.size; r++)
{
for (var c = 0; c < grid.size; c++)
{
//TODO: load data.
//if (Math.random() > .9)
// grid.set(r, c, players[getRandomInt(0, players.length)]);
}
}
var frameCount = 0;
//TODO: current player index
var user = renderer.getPlayer(0);
renderer.initUser(user);
function update()
{
renderer.update();
}
//Thanks to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function paintLoop(ctx)
{
renderer.paint(ctx); //TODO: pre-rendering.
//TODO: sync each loop with server. (server will give frame count.)
frameCount++;
update();
requestAnimationFrame(paintLoop);
}
paintLoop(ctx2);
//Event listeners
$(document).keydown(function(e) {
if (user.dead) if (user.dead)
return; return;
var newHeading = -1; var newHeading = -1;
@ -115,9 +46,108 @@ $(function() {
if (newHeading === user.currentHeading || ((newHeading % 2 === 0) ^ if (newHeading === user.currentHeading || ((newHeading % 2 === 0) ^
(user.currentHeading % 2 === 0))) (user.currentHeading % 2 === 0)))
{ {
//TODO: notify server.
user.heading = newHeading; user.heading = newHeading;
if (socket)
socket.emit("frame", {
frame: frame,
heading: newHeading
}, function(success, msg) {
if (!success)
{
//TODO: restore frames.
console.error(msg);
}
});
} }
e.preventDefault(); e.preventDefault();
}); });
$(function() {
var grid = renderer.grid;
//Socket connection.
socket = require('socket.io-client')('http://paper-io-thekidofarcrania.c9users.io:8081');
socket.on('connect', function(){
console.info("Connected to server.");
socket.emit('hello', {
name: 'Test player',
type: 0, //Free-for-all
gameid: -1 //Requested game-id, or -1 for anyone.
}, function(success) {
if (success) console.info("Connected to game!");
else console.error("Unable to connect to game.");
});
});
socket.on('game', function(data){
//Initialize game.
//TODO: display data.gameid --- game id #
renderer.reset();
//Load players.
data.players.forEach(function(p) {
renderer.addPlayer(new Player(true, grid, p));
});
user = renderer.getPlayerFromNum(data.num);
renderer.setUser(user);
//Load grid.
var gridData = new Uint8Array(data.grid);
for (var r = 0; r < grid.size; r++)
for (var c = 0; c < grid.size; c++)
{
var ind = gridData[r * grid.size + c] - 1;
grid.set(r, c, ind === -1 ? null : renderer.getPlayer(ind));
}
frame = data.frame;
});
socket.on('notifyFrame', function(data) {
if (data.frame - 1 !== frame)
{
console.error("Frames don't match up!");
socket.emit('requestFrame'); //Restore data.
return;
}
frame++;
if (data.newPlayers)
{
data.newPlayers.forEach(function(p) {
renderer.addPlayer(new Player(true, grid, p));
});
}
data.moves.forEach(function(val, i) {
if (renderer.getPlayer(val) !== user)
renderer.getPlayer(i).heading = val.heading;
});
paintLoop();
});
socket.on('disconnect', function(){
console.info("Server has disconnected. Creating new game.");
});
var deadFrames = 0;
function paintLoop()
{
renderer.paint();
if (user.dead && deadFrames === 60) //One second of frames
{
//TODO: Show welcome screen.
deadFrames = 0;
return;
}
renderer.update();
if (user.dead)
{
socket.disconnect();
deadFrames++;
requestAnimationFrame(paintLoop);
}
}
}); });

View File

@ -10,7 +10,8 @@ var consts = {
GRID_SIZE: constant(80), GRID_SIZE: constant(80),
CELL_WIDTH: constant(40), CELL_WIDTH: constant(40),
SPEED: constant(5), SPEED: constant(5),
BORDER_WIDTH: constant(20) BORDER_WIDTH: constant(20),
MAX_PLAYERS: constant(255)
}; };
Object.defineProperties(module.exports, consts); Object.defineProperties(module.exports, consts);

View File

@ -28,7 +28,9 @@ exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill)
if (newPlayerFrames[val.num] < ANIMATE_FRAMES) if (newPlayerFrames[val.num] < ANIMATE_FRAMES)
newPlayerFrames[val.num]++; newPlayerFrames[val.num]++;
else else
{
val.move(); val.move();
}
if (val.dead) if (val.dead)
adead.push(val); adead.push(val);

View File

@ -35,13 +35,12 @@ $(function () {
gameHeight = canvasHeight - BAR_HEIGHT; gameHeight = canvasHeight - BAR_HEIGHT;
}); });
var animateGrid = new Grid(GRID_SIZE);
var allowAnimation = true; var allowAnimation = true;
var players = []; var animateGrid, players, allPlayers, newPlayerFrames, playerPortion, grid,
var allPlayers = []; animateTo, offset, user, lagPortion, portionSpeed, zoom, kills, showedDead;
var newPlayerFrames = [];
var playerPortion = []; grid = new Grid(GRID_SIZE, function(row, col, before, after) {
var grid = new Grid(GRID_SIZE, function(row, col, before, after) {
//Keep track of areas. //Keep track of areas.
if (before) if (before)
playerPortion[before.num]--; playerPortion[before.num]--;
@ -58,16 +57,27 @@ var grid = new Grid(GRID_SIZE, function(row, col, before, after) {
}); });
}); });
var animateTo = [0, 0]; function init() {
var offset = [0, 0]; animateGrid = new Grid(GRID_SIZE);
grid.reset();
var user; players = [];
var lagPortion = 0; allPlayers = [];
var portionSpeed = 0; newPlayerFrames = [];
var zoom = 1; playerPortion = [];
var kills = 0;
var showedDead = false; animateTo = [0, 0];
offset = [0, 0];
user = null;
lagPortion = 0;
portionSpeed = 0;
zoom = 1;
kills = 0;
showedDead = false;
}
init();
//Paint methods. //Paint methods.
function paintGridBorder(ctx) function paintGridBorder(ctx)
@ -207,17 +217,17 @@ function paintUIBar(ctx)
var barOffset; var barOffset;
ctx.fillStyle = "white"; ctx.fillStyle = "white";
ctx.font = "24px Changa"; ctx.font = "24px Changa";
barOffset = ctx.measureText(user.name).width + 20; barOffset = ctx.measureText(user ? user.name : "").width + 20;
ctx.fillText(user.name, 5, CELL_WIDTH - 5); ctx.fillText(user ? user.name : "", 5, CELL_WIDTH - 5);
//Draw filled bar. //Draw filled bar.
ctx.fillStyle = "rgba(180, 180, 180, .3)"; ctx.fillStyle = "rgba(180, 180, 180, .3)";
ctx.fillRect(barOffset, 0, BAR_WIDTH, BAR_HEIGHT); ctx.fillRect(barOffset, 0, BAR_WIDTH, BAR_HEIGHT);
var barSize = Math.ceil((BAR_WIDTH - MIN_BAR_WIDTH) * lagPortion + MIN_BAR_WIDTH); var barSize = Math.ceil((BAR_WIDTH - MIN_BAR_WIDTH) * lagPortion + MIN_BAR_WIDTH);
ctx.fillStyle = user.baseColor.rgbString(); ctx.fillStyle = user ? user.baseColor.rgbString() : "";
ctx.fillRect(barOffset, 0, barSize, CELL_WIDTH); ctx.fillRect(barOffset, 0, barSize, CELL_WIDTH);
ctx.fillStyle = user.shadowColor.rgbString(); ctx.fillStyle = user ? user.shadowColor.rgbString() : "";
ctx.fillRect(barOffset, CELL_WIDTH, barSize, SHADOW_OFFSET); ctx.fillRect(barOffset, CELL_WIDTH, barSize, SHADOW_OFFSET);
//Percentage //Percentage
@ -274,7 +284,7 @@ function paint(ctx)
ctx.restore(); ctx.restore();
paintUIBar(ctx); paintUIBar(ctx);
if (user.dead && !showedDead) if ((!user || user.dead) && !showedDead)
{ {
showedDead = true; showedDead = true;
console.log("You died!"); console.log("You died!");
@ -302,7 +312,10 @@ function update() {
} }
//Change area percentage //Change area percentage
var userPortion = playerPortion[user.num] / (GRID_SIZE * GRID_SIZE); var userPortion;
if (user) userPortion = playerPortion[user.num] / (GRID_SIZE * GRID_SIZE);
else userPortion = 0;
if (lagPortion !== userPortion) if (lagPortion !== userPortion)
{ {
delta = userPortion - lagPortion; delta = userPortion - lagPortion;
@ -330,7 +343,7 @@ function update() {
//TODO: animate player is dead. (maybe explosion?), and tail rewinds itself. //TODO: animate player is dead. (maybe explosion?), and tail rewinds itself.
//TODO: show when this player is dead //TODO: show when this player is dead
centerOnPlayer(user, animateTo); if (user) centerOnPlayer(user, animateTo);
} }
//Helper methods. //Helper methods.
@ -381,6 +394,8 @@ function getBounceOffset(frame)
module.exports = exports = { module.exports = exports = {
addPlayer: function(player) { addPlayer: function(player) {
if (allPlayers[player.num])
return; //Already added.
allPlayers[player.num] = players[players.length] = player; allPlayers[player.num] = players[players.length] = player;
newPlayerFrames[player.num] = 0; newPlayerFrames[player.num] = 0;
playerPortion[player.num] = 0; playerPortion[player.num] = 0;
@ -390,16 +405,22 @@ module.exports = exports = {
getPlayer: function(ind) { getPlayer: function(ind) {
return players[ind]; return players[ind];
}, },
playerNum: function() { getPlayerFromNum: function(num) {
return allPlayers[num];
},
playerSize: function() {
return players.length; return players.length;
}, },
initUser: function(player) { setUser: function(player) {
user = player; user = player;
centerOnPlayer(user, offset); centerOnPlayer(user, offset);
}, },
incrementKill: function() { incrementKill: function() {
kills++; kills++;
}, },
reset: function() {
init();
},
paint: paintDoubleBuff, paint: paintDoubleBuff,
update: update update: update
}; };

View File

@ -1,10 +1,12 @@
var GRID_SIZE = 80;
var CELL_WIDTH = 40;
var MAX_PLAYERS = 255;
var Grid = require("./grid.js"); var Grid = require("./grid.js");
var Player = require("./player.js"); var Player = require("./player.js");
var core = require("./game-core.js"); var core = require("./game-core.js");
var consts = require("./game-consts.js");
var GRID_SIZE = consts.GRID_SIZE;
var CELL_WIDTH = consts.CELL_WIDTH;
var MAX_PLAYERS = consts.MAX_PLAYERS;
function Game(id) function Game(id)
{ {
@ -50,7 +52,7 @@ function Game(id)
newPlayers.push(p); newPlayers.push(p);
newPlayerFrames.push(p); newPlayerFrames.push(p);
nextInd++; nextInd++;
core.initPlayer(p); core.initPlayer(grid, p);
var splayers = players.map(function(val) {return val.serialData();}); var splayers = players.map(function(val) {return val.serialData();});
client.emit("game", { client.emit("game", {
@ -60,11 +62,12 @@ function Game(id)
"players": splayers, "players": splayers,
"grid": gridSerialData(grid, players) "grid": gridSerialData(grid, players)
}); });
console.log(p.name + " joined.");
//TODO: limit number of requests per frame. //TODO: limit number of requests per frame.
client.on("requestFrame", function (fn) { client.on("requestFrame", function () {
var splayers = players.map(function(val) {return val.serialData();}); var splayers = players.map(function(val) {return val.serialData();});
fn({ client.emit("game", {
"num": p.num, "num": p.num,
"gameid": id, "gameid": id,
"frame": frame, "frame": frame,
@ -72,6 +75,7 @@ function Game(id)
"grid": gridSerialData(grid, players) "grid": gridSerialData(grid, players)
}); });
}); });
client.on("frame", function(data, errorHan){ client.on("frame", function(data, errorHan){
if (typeof data === "function") if (typeof data === "function")
{ {
@ -86,8 +90,8 @@ function Game(id)
errorHan(false, "No data supplied."); errorHan(false, "No data supplied.");
else if (!checkInt(data.frame, 0, Infinity)) else if (!checkInt(data.frame, 0, Infinity))
errorHan(false, "Requires a valid non-negative frame integer."); errorHan(false, "Requires a valid non-negative frame integer.");
else if (data.frame < frame) //else if (data.frame < frame)
errorHan(false, "Late frame received."); // errorHan(false, "Late frame received.");
else if (data.frame > frame) else if (data.frame > frame)
errorHan(false, "Invalid frame received."); errorHan(false, "Invalid frame received.");
else else
@ -109,10 +113,11 @@ function Game(id)
this.tickFrame = function() { this.tickFrame = function() {
//TODO: notify those that drop out.
var snews = newPlayers.map(function(val) {return val.serialData();}); var snews = newPlayers.map(function(val) {return val.serialData();});
var moves = players.map(function(val) {return {heading: val.heading};}); var moves = players.map(function(val) {return {heading: val.heading};});
var data = {moves: moves}; var data = {frame: frame + 1, moves: moves};
if (snews.length > 0) if (snews.length > 0)
{ {
data.newPlayers = snews; data.newPlayers = snews;
@ -129,7 +134,10 @@ function Game(id)
{ {
var dead = []; var dead = [];
core.updateFrame(grid, players, newPlayerFrames, dead); core.updateFrame(grid, players, newPlayerFrames, dead);
dead.forEach(function(val) { val.client.disconnect(true); }); dead.forEach(function(val) {
console.log(val.name + " died.");
val.client.disconnect(true);
});
} }
} }

10
grid.js
View File

@ -1,6 +1,7 @@
function Grid(size, changeCallback) function Grid(size, changeCallback)
{ {
var grid = new Array(size); var grid = new Array(size);
var modified = false;
var data = { var data = {
grid: grid, grid: grid,
@ -26,8 +27,17 @@ function Grid(size, changeCallback)
if (typeof changeCallback === "function") if (typeof changeCallback === "function")
changeCallback(row, col, before, value); changeCallback(row, col, before, value);
modified = true;
return before; return before;
} }
this.reset = function() {
if (modified)
{
grid = new Array(size);
modified = false;
}
}
this.isOutOfBounds = isOutOfBounds.bind(this, data); this.isOutOfBounds = isOutOfBounds.bind(this, data);

View File

@ -1,6 +1,10 @@
var Stack = require("./stack.js"); var Stack = require("./stack.js");
var Color = require("./color.js"); var Color = require("./color.js");
var Grid = require("./grid.js"); var Grid = require("./grid.js");
var consts = require("./game-consts.js");
var GRID_SIZE = consts.GRID_SIZE;
var CELL_WIDTH = consts.CELL_WIDTH;
function defineGetter(getter) { function defineGetter(getter) {
return { return {
@ -24,9 +28,6 @@ function defineAccessorProperties(thisobj, data /*, names...*/)
Object.defineProperties(thisobj, descript); Object.defineProperties(thisobj, descript);
} }
var CELL_WIDTH = 40;
var GRID_SIZE = 80;
function TailMove(orientation) function TailMove(orientation)
{ {
this.move = 1; this.move = 1;
@ -53,7 +54,7 @@ function Tail(player, sdata)
{ {
data.startRow = data.prevRow = sdata.startRow || 0; data.startRow = data.prevRow = sdata.startRow || 0;
data.startCol = data.prevCol = sdata.startCol || 0; data.startCol = data.prevCol = sdata.startCol || 0;
sdata.forEach(function(val) { sdata.tail.forEach(function(val) {
addTail(data, val.orientation, val.move); addTail(data, val.orientation, val.move);
}); });
} }
@ -68,11 +69,11 @@ function Tail(player, sdata)
//Instance methods. //Instance methods.
function serialData(data) { function serialData(data) {
return JSON.serialize({ return {
tail: data.tail, tail: data.tail,
startRow: data.startRow, startRow: data.startRow,
startCol: data.startCol startCol: data.startCol
}); };
} }
function setTailGrid(data, tailGrid, r, c) function setTailGrid(data, tailGrid, r, c)
@ -430,7 +431,7 @@ Player.prototype.render = function(ctx, fade)
var mid = CELL_WIDTH / 2; var mid = CELL_WIDTH / 2;
var grd = ctx.createRadialGradient(this.posX + mid, this.posY + mid - SHADOW_OFFSET, 1, var grd = ctx.createRadialGradient(this.posX + mid, this.posY + mid - SHADOW_OFFSET, 1,
this.posX + mid, this.posY + mid - SHADOW_OFFSET, CELL_WIDTH); this.posX + mid, this.posY + mid - SHADOW_OFFSET, CELL_WIDTH);
grd.addColorStop(0, this.baseColor.rgbString().deriveAlpha(fade)); grd.addColorStop(0, this.baseColor.deriveAlpha(fade).rgbString());
grd.addColorStop(1, new Color(0, 0, 1, fade).rgbString()); grd.addColorStop(1, new Color(0, 0, 1, fade).rgbString());
ctx.fillStyle = grd; ctx.fillStyle = grd;
ctx.fillRect(this.posX - 1, this.posY - SHADOW_OFFSET, CELL_WIDTH + 2, CELL_WIDTH); ctx.fillRect(this.posX - 1, this.posY - SHADOW_OFFSET, CELL_WIDTH + 2, CELL_WIDTH);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -8,17 +8,24 @@ var serve = serveStatic('public/', {'cacheControl': false});
// Create server // Create server
var server = http.createServer(function onRequest (req, res) { var server = http.createServer(function onRequest (req, res) {
serve(req, res, finalhandler(req, res)) serve(req, res, finalhandler(req, res))
}) });
// Listen // Listen
server.listen(8080); server.listen(8080);
var server = http.createServer(); server = http.createServer();
var io = require('socket.io')(server); var io = require('socket.io')(server);
var games = []; var Game = require('./game-server.js');
var games = [new Game()];
io.on('connection', function(socket){ io.on('connection', function(socket){
socket.emit('message', 'hello.'); // emit an event to the socket socket.on("hello", function(data, fn) {
io.emit('message', 'new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.new person.'); // emit an event to all connected sockets //TODO: error checking.
socket.on('reply', function(){ /* */ }); // listen to the event fn(true);
games[0].addPlayer(socket, data.name);
});
}); });
server.listen(8081); server.listen(8081);
setInterval(function() {
games[0].tickFrame();
}, 1000 / 60);