Optimize flood-fill and fix OBOB + player bug

The flood-fill had allocated a good amount of memory ~1MB each time we visit a square. This is reduced to the grid size squared, only allocated if the square we are on now is valid.

The OBOB error occured when server was updating one time too few when a player is initialized.

The player bug occurs when a new player joins the game. Normally each new player gets about 60ish frames of lag to start up. However, the new player client incorrectly assumes all other players are new as well.
This commit is contained in:
theKidOfArcrania 2017-03-01 00:56:49 +00:00
parent e8fd5d1f7c
commit 8352433fa5
7 changed files with 236 additions and 197 deletions

View File

@ -120,6 +120,7 @@ function connectServer() {
renderer.addPlayer(pl);
});
user = renderer.getPlayerFromNum(data.num);
if (!user) throw new Error();
renderer.setUser(user);
//Load grid.
@ -136,7 +137,7 @@ function connectServer() {
});
var waiting = false;
socket.on('notifyFrame', function(data, fn) {
socket.on('notifyFrame', function(data) {
if (timeout != undefined)
clearTimeout(timeout);
@ -149,6 +150,7 @@ function connectServer() {
socket.emit('requestFrame'); //Restore data.
waiting = true;
return;
//TODO: cache frames when this happen.
}
frame++;
@ -184,6 +186,19 @@ function connectServer() {
renderer.update(frame);
var locs = {};
for (var i = 0; i < renderer.playerSize(); i++)
{
var p = renderer.getPlayer(i);
locs[p.num] = [p.posX, p.posY, p.waitLag];
}
socket.emit("verify", {
frame: frame,
locs: locs
}, function(frame, success, msg) {
if (!success) console.error(frame + ": " + msg);
}.bind(this, frame));
dirty = true;
requestAnimationFrame(function() {
paintLoop();
@ -191,8 +206,7 @@ function connectServer() {
timeout = setTimeout(function() {
console.warn("Server has timed-out. Disconnecting.");
socket.disconnect();
}, 500);
fn();
}, 3000);
});
socket.on('disconnect', function(){

View File

@ -1,6 +1,5 @@
var ANIMATE_FRAMES = 24;
var CELL_WIDTH = 40;
var NEW_PLAYER_LAG = 60; //wait for a second at least.
//TODO: remove constants.
exports.initPlayer = function(grid, player)
@ -10,7 +9,7 @@ exports.initPlayer = function(grid, player)
if (!grid.isOutOfBounds(dr + player.row, dc + player.col))
grid.set(dr + player.row, dc + player.col, player);
};
exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill, frame)
exports.updateFrame = function(grid, players, dead, notifyKill)
{
var adead = [];
if (dead instanceof Array)
@ -24,15 +23,7 @@ exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill,
//Move players.
var tmp = players.filter(function(val) {
if (!newPlayerFrames[val.num])
newPlayerFrames[val.num] = 0;
if (newPlayerFrames[val.num] < ANIMATE_FRAMES + NEW_PLAYER_LAG)
newPlayerFrames[val.num]++;
else
//TODO: remove frame
val.move(frame);
val.move();
if (val.dead)
adead.push(val);
return !val.dead;
@ -102,12 +93,12 @@ exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill,
}
tmp = tmp.filter(function(val, i) {
if (removing[i] && val.num !== 1) /** GHOST PLAYER **/
if (removing[i])
{
adead.push(val);
val.die();
}
return !removing[i] || val.num === 1;
return !removing[i];
});
players.length = tmp.length;
for (var i = 0; i < tmp.length; i++)

View File

@ -37,7 +37,7 @@ $(function () {
var allowAnimation = true;
var animateGrid, players, allPlayers, newPlayerFrames, playerPortion, grid,
var animateGrid, players, allPlayers, playerPortion, grid,
animateTo, offset, user, lagPortion, portionSpeed, zoom, kills, showedDead;
grid = new Grid(GRID_SIZE, function(row, col, before, after) {
@ -63,7 +63,6 @@ function init() {
players = [];
allPlayers = [];
newPlayerFrames = [];
playerPortion = [];
animateTo = [0, 0];
@ -91,23 +90,6 @@ function paintGridBorder(ctx)
ctx.fillRect(-BORDER_WIDTH, gridWidth, gridWidth + BORDER_WIDTH * 2, BORDER_WIDTH);
}
function paintGridLines(ctx)
{
ctx.fillStyle = 'lightgray';
ctx.beginPath();
for (var x = modRotate(-offset[0], CELL_WIDTH); x < gameWidth; x += CELL_WIDTH)
{
ctx.moveTo(x, 0);
ctx.lineTo(x, gameHeight);
}
for (var y = modRotate(-offset[1], CELL_WIDTH); y < gameHeight; y+= CELL_WIDTH)
{
ctx.moveTo(0, y);
ctx.lineTo(gameWidth, y);
}
ctx.stroke();
}
function paintGrid(ctx)
{
//Paint background.
@ -208,6 +190,7 @@ function paintGrid(ctx)
}
}
function paintUIBar(ctx)
{
//UI Bar background
@ -249,10 +232,37 @@ function paintUIBar(ctx)
if (a.portion === b.portion) return a.player.num - b.player.num;
else return b.portion - a.portion;
});
var rank = sorted.findIndex(function(val) {return val.player === user});
ctx.fillText("Rank: " + (rank === -1 ? "--" : rank + 1) + " of " + sorted.length,
ctx.measureText(killsText).width + killsOffset + 20, CELL_WIDTH - 5);
//Show leaderboard.
var maxPortion = sorted[0] ? sorted[0].portion : 0;
var leaderboardNum = Math.min(5, sorted.length);
for (var i = 0; i < leaderboardNum; i++)
{
var player = sorted[i].player;
var name = player.name;
var portion = sorted[i].portion / maxPortion;
var nameWidth = ctx.measureText(name).width;
barSize = Math.ceil((BAR_WIDTH - MIN_BAR_WIDTH) * portion + MIN_BAR_WIDTH);
var barX = canvasWidth - barSize;
var barY = BAR_HEIGHT * (i + 1);
ctx.fillStyle = 'rgba(10, 10, 10, .3)';
ctx.fillRect(barX - 10, barY, barSize + 10, CELL_WIDTH + 10);
ctx.fillStyle = player.baseColor.rgbString();
ctx.fillRect(barX, barY, barSize, CELL_WIDTH);
ctx.fillStyle = player.shadowColor.rgbString();
ctx.fillRect(barX, barY + CELL_WIDTH, barSize, SHADOW_OFFSET);
ctx.fillStyle = "black";
ctx.fillText(name, barX - nameWidth - 5, barY + CELL_WIDTH - 10);
}
}
//TODO: depict leaderboard.
@ -274,7 +284,7 @@ function paint(ctx)
paintGrid(ctx);
players.forEach(function (p) {
var fr = newPlayerFrames[p.num] || 0;
var fr = p.waitLag;
if (fr < ANIMATE_FRAMES)
p.render(ctx, fr / ANIMATE_FRAMES);
else
@ -343,11 +353,11 @@ function update(frame) {
portionSpeed = Math.abs(userPortion - lagPortion) / ANIMATE_FRAMES;
var dead = [];
core.updateFrame(grid, players, newPlayerFrames, dead, function addKill(killer, other)
core.updateFrame(grid, players, dead, function addKill(killer, other)
{
if (players[killer] === user && killer !== other)
kills++;
}, frame);
});
dead.forEach(function(val) {
console.log(val.name + " is dead");
allPlayers[val.num] = undefined;
@ -359,15 +369,6 @@ function update(frame) {
}
//Helper methods.
function modRotate(val, mod)
{
var res = val % mod;
if (res >= 0)
return res;
else
return mod + res;
}
function centerOnPlayer(player, pos)
{
var xOff = Math.floor(player.posX - (gameWidth / zoom - CELL_WIDTH) / 2);
@ -409,7 +410,6 @@ module.exports = exports = {
if (allPlayers[player.num])
return; //Already added.
allPlayers[player.num] = players[players.length] = player;
newPlayerFrames[player.num] = 0;
playerPortion[player.num] = 0;
return players.length - 1;
},

View File

@ -2,7 +2,7 @@
var Color = require("./color");
var Grid = require("./grid");
var Player = require("./player");
var Gate = require("./gate");
//var Gate = require("./gate");
var core = require("./game-core");
var consts = require("./game-consts");
@ -36,7 +36,7 @@ function Game(id)
var nextInd = 0;
var players = [];
var newPlayers = [];
var newPlayerFrames = [];
var frameLocs = [];
var frame = 0;
var filled = 0;
@ -76,19 +76,9 @@ function Game(id)
p.client = client;
players.push(p);
newPlayers.push(p);
newPlayerFrames.push(0);
nextInd++;
core.initPlayer(grid, p);
var splayers = players.map(function(val) {return val.serialData();});
client.emit("game", {
"num": p.num,
"gameid": id,
"frame": frame,
"players": splayers,
"grid": gridSerialData(grid, players),
"colors": colors
});
//playerReady(p, frame);
console.log(p.name + " joined.");
@ -109,6 +99,20 @@ function Game(id)
//playerReady(p, frame);
});
client.on("verify", function(data, resp) {
if (typeof resp !== "function")
return;
if (!data.frame)
resp(false, "No frame supplied");
else if (!checkInt(data.frame, 0, frame + 1))
resp(false, "Must be a valid frame number");
else
{
verifyPlayerLocations(data.frame, data.locs, resp);
}
});
client.on("frame", function(data, errorHan){
if (typeof data === "function")
{
@ -123,8 +127,6 @@ function Game(id)
errorHan(false, "No data supplied.");
else if (!checkInt(data.frame, 0, Infinity))
errorHan(false, "Requires a valid non-negative frame integer.");
//else if (data.frame < frame)
// errorHan(false, "Late frame received.");
else if (data.frame > frame)
errorHan(false, "Invalid frame received.");
else
@ -149,36 +151,74 @@ function Game(id)
return true;
};
/*
var ready = 0;
var readyTick = false;
function playerReady(player, waitFrame)
function pushPlayerLocations()
{
if (player.frame < waitFrame)
{
ready++;
player.frame = waitFrame;
}
tick();
var locs = [];
for (var p of players)
locs[p.num] = [p.posX, p.posY, p.waitLag];
locs.frame = frame;
if (frameLocs.length >= 100)
frameLocs.shift();
frameLocs.push(locs);
}
*/
function verifyPlayerLocations(fr, verify, resp)
{
var minFrame = frame - frameLocs.length + 1;
if (fr < minFrame || fr > frame)
{
resp(false, "Frames out of reference");
return;
}
function string(loc)
{
return '(' + loc[0] + ', ' + loc[1] + ') [' + loc[2] + ']';
}
var locs = frameLocs[fr - minFrame];
if (locs.frame !== fr)
{
resp(false, locs.frame + " != " + fr);
return;
}
for (var num in verify)
{
if (locs[num][0] !== verify[num][0] || locs[num][1] !== verify[num][1] || locs[num][2] !== verify[num][2])
{
resp(false, 'P' + num + ' ' + string(locs[num]) + ' !== ' + string(verify[num]));
return;
}
}
resp(true);
}
function tick() {
//if (readyTick && ready === players.length)
//{
// ready = 0;
// readyTick = false;
//}else
// return;
//TODO: notify those players that this server automatically drops out.
var snews = newPlayers.map(function(val) {return val.serialData();});
var splayers = players.map(function(val) {return val.serialData();});
var snews = newPlayers.map(function(val) {
//Emit game stats.
val.client.emit("game", {
"num": val.num,
"gameid": id,
"frame": frame,
"players": splayers,
"grid": gridSerialData(grid, players),
"colors": colors
});
return val.serialData();
});
var moves = players.map(function(val) {
//Account for race condition (when heading is set after emitting frames, and before updating).
val.heading = val.tmpHeading;
return {num: val.num, left: !!val.disconnected, heading: val.heading};
});
update();
var data = {frame: frame + 1, moves: moves};
if (snews.length > 0)
{
@ -186,45 +226,25 @@ function Game(id)
newPlayers = [];
}
//TODO: send a "good-bye" frame to the dead players. Just in case.
players.forEach(function(val) {
if (val.num === 1) //GHOST PLAYER
{
var splayers = players.map(function(val) {return val.serialData();});
val.client.emit("game", {
"num": val.num,
"gameid": id,
"frame": frame,
"players": splayers,
"grid": gridSerialData(grid, players),
"colors": colors
});
}
else
{
val.client.emit("notifyFrame", data, function() {
//playerReady(val, waitFrame);
});
}
});
for (var pl of players)
pl.client.emit("notifyFrame", data);
frame++;
setTimeout(update, 1);
pushPlayerLocations();
}
this.tickFrame = function() {
//readyTick = true;
tick();
};
this.tickFrame = tick;
function update()
{
var dead = [];
core.updateFrame(grid, players, newPlayerFrames, dead, undefined, frame);
dead.forEach(function(val) {
console.log(val.name + " died.");
//val.client.disconnect(true);
});
core.updateFrame(grid, players, dead);
for (var pl of dead)
{
//TODO: send a "good-bye" frame to the dead players. Just in case.
console.log(pl.name + " died.");
pl.client.disconnect(true);
}
}
}

View File

@ -5,6 +5,7 @@ var consts = require("./game-consts.js");
var GRID_SIZE = consts.GRID_SIZE;
var CELL_WIDTH = consts.CELL_WIDTH;
var NEW_PLAYER_LAG = 60; //wait for a second at least.
function defineGetter(getter) {
return {
@ -291,13 +292,17 @@ function fillTail(data)
function floodFill(data, grid, row, col, been)
{
var coords = [];
var filled = new Stack(40000);
var surrounded = true;
function onTail(c) { return data.tailGrid[c[0]] && data.tailGrid[c[0]][c[1]]; }
coords.push([row, col]);
var start = [row, col];
if (grid.isOutOfBounds(r, c) || been.get(row, col) || onTail(start) || grid.get(row, col) === data.player)
return; //Avoid allocating too many resources.
var coords = [];
var filled = new Stack(GRID_SIZE * GRID_SIZE + 1);
var surrounded = true;
coords.push(start);
while (coords.length > 0)
{
var coord = coords.shift();
@ -357,6 +362,7 @@ function Player(isClient, grid, sdata) {
data.posX = sdata.posX;
data.posY = sdata.posY;
this.heading = data.currentHeading = sdata.currentHeading; //0 is up, 1 is right, 2 is down, 3 is left.
data.waitLag = sdata.waitLag || 0;
data.dead = false;
//Only need colors for client side.
@ -386,7 +392,7 @@ function Player(isClient, grid, sdata) {
//Instance methods.
this.move = move.bind(this, data);
this.die = function() { if (data.num !== 1) /* GHOST PLAYER */ data.dead = true;};
this.die = function() { data.dead = true;};
this.serialData = function() {
return {
num: data.num,
@ -394,12 +400,13 @@ function Player(isClient, grid, sdata) {
posX: data.posX,
posY: data.posY,
currentHeading: data.currentHeading,
tail: data.tail.serialData()
tail: data.tail.serialData(),
waitLag: data.waitLag
};
};
//Read-only Properties.
defineAccessorProperties(this, data, "currentHeading", "dead", "name", "num", "posX", "posY", "grid", "tail");
defineAccessorProperties(this, data, "currentHeading", "dead", "name", "num", "posX", "posY", "grid", "tail", "waitLag");
Object.defineProperties(this, {
row: defineGetter(function() { return calcRow(data); }),
col: defineGetter(function() { return calcCol(data); })
@ -453,12 +460,15 @@ Player.prototype.render = function(ctx, fade)
};
function move(data, frame)
function move(data)
{
//console.log("P" + this.num + ": " + frame + ": " + this.heading);
if (data.waitLag < NEW_PLAYER_LAG)
{
data.waitLag++;
return;
}
//Move to new position.
var prevX = this.posX, prevY = this.posY;
var heading = this.heading;
if (this.posX % CELL_WIDTH !== 0 || this.posY % CELL_WIDTH !== 0)
heading = data.currentHeading;
@ -476,12 +486,6 @@ function move(data, frame)
var row = this.row, col = this.col;
if (data.grid.isOutOfBounds(row, col))
{
if (data.num === 1) /* GHOST PLAYER */
{
prevX = this.posX;
prevY = this.posY;
return;
}
data.dead = true;
return;
}

View File

@ -406,6 +406,7 @@ function connectServer() {
renderer.addPlayer(pl);
});
user = renderer.getPlayerFromNum(data.num);
if (!user) throw new Error();
renderer.setUser(user);
//Load grid.
@ -422,7 +423,7 @@ function connectServer() {
});
var waiting = false;
socket.on('notifyFrame', function(data, fn) {
socket.on('notifyFrame', function(data) {
if (timeout != undefined)
clearTimeout(timeout);
@ -435,6 +436,7 @@ function connectServer() {
socket.emit('requestFrame'); //Restore data.
waiting = true;
return;
//TODO: cache frames when this happen.
}
frame++;
@ -470,6 +472,19 @@ function connectServer() {
renderer.update(frame);
var locs = {};
for (var i = 0; i < renderer.playerSize(); i++)
{
var p = renderer.getPlayer(i);
locs[p.num] = [p.posX, p.posY, p.waitLag];
}
socket.emit("verify", {
frame: frame,
locs: locs
}, function(frame, success, msg) {
if (!success) console.error(frame + ": " + msg);
}.bind(this, frame));
dirty = true;
requestAnimationFrame(function() {
paintLoop();
@ -477,8 +492,7 @@ function connectServer() {
timeout = setTimeout(function() {
console.warn("Server has timed-out. Disconnecting.");
socket.disconnect();
}, 500);
fn();
}, 3000);
});
socket.on('disconnect', function(){
@ -547,7 +561,6 @@ Object.defineProperties(module.exports, consts);
},{}],6:[function(require,module,exports){
var ANIMATE_FRAMES = 24;
var CELL_WIDTH = 40;
var NEW_PLAYER_LAG = 60; //wait for a second at least.
//TODO: remove constants.
exports.initPlayer = function(grid, player)
@ -557,7 +570,7 @@ exports.initPlayer = function(grid, player)
if (!grid.isOutOfBounds(dr + player.row, dc + player.col))
grid.set(dr + player.row, dc + player.col, player);
};
exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill, frame)
exports.updateFrame = function(grid, players, dead, notifyKill)
{
var adead = [];
if (dead instanceof Array)
@ -571,15 +584,7 @@ exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill,
//Move players.
var tmp = players.filter(function(val) {
if (!newPlayerFrames[val.num])
newPlayerFrames[val.num] = 0;
if (newPlayerFrames[val.num] < ANIMATE_FRAMES + NEW_PLAYER_LAG)
newPlayerFrames[val.num]++;
else
//TODO: remove frame
val.move(frame);
val.move();
if (val.dead)
adead.push(val);
return !val.dead;
@ -649,12 +654,12 @@ exports.updateFrame = function(grid, players, newPlayerFrames, dead, notifyKill,
}
tmp = tmp.filter(function(val, i) {
if (removing[i] && val.num !== 1) /** GHOST PLAYER **/
if (removing[i])
{
adead.push(val);
val.die();
}
return !removing[i] || val.num === 1;
return !removing[i];
});
players.length = tmp.length;
for (var i = 0; i < tmp.length; i++)
@ -729,7 +734,7 @@ $(function () {
var allowAnimation = true;
var animateGrid, players, allPlayers, newPlayerFrames, playerPortion, grid,
var animateGrid, players, allPlayers, playerPortion, grid,
animateTo, offset, user, lagPortion, portionSpeed, zoom, kills, showedDead;
grid = new Grid(GRID_SIZE, function(row, col, before, after) {
@ -755,7 +760,6 @@ function init() {
players = [];
allPlayers = [];
newPlayerFrames = [];
playerPortion = [];
animateTo = [0, 0];
@ -783,23 +787,6 @@ function paintGridBorder(ctx)
ctx.fillRect(-BORDER_WIDTH, gridWidth, gridWidth + BORDER_WIDTH * 2, BORDER_WIDTH);
}
function paintGridLines(ctx)
{
ctx.fillStyle = 'lightgray';
ctx.beginPath();
for (var x = modRotate(-offset[0], CELL_WIDTH); x < gameWidth; x += CELL_WIDTH)
{
ctx.moveTo(x, 0);
ctx.lineTo(x, gameHeight);
}
for (var y = modRotate(-offset[1], CELL_WIDTH); y < gameHeight; y+= CELL_WIDTH)
{
ctx.moveTo(0, y);
ctx.lineTo(gameWidth, y);
}
ctx.stroke();
}
function paintGrid(ctx)
{
//Paint background.
@ -900,6 +887,7 @@ function paintGrid(ctx)
}
}
function paintUIBar(ctx)
{
//UI Bar background
@ -941,10 +929,37 @@ function paintUIBar(ctx)
if (a.portion === b.portion) return a.player.num - b.player.num;
else return b.portion - a.portion;
});
var rank = sorted.findIndex(function(val) {return val.player === user});
ctx.fillText("Rank: " + (rank === -1 ? "--" : rank + 1) + " of " + sorted.length,
ctx.measureText(killsText).width + killsOffset + 20, CELL_WIDTH - 5);
//Show leaderboard.
var maxPortion = sorted[0] ? sorted[0].portion : 0;
var leaderboardNum = Math.min(5, sorted.length);
for (var i = 0; i < leaderboardNum; i++)
{
var player = sorted[i].player;
var name = player.name;
var portion = sorted[i].portion / maxPortion;
var nameWidth = ctx.measureText(name).width;
barSize = Math.ceil((BAR_WIDTH - MIN_BAR_WIDTH) * portion + MIN_BAR_WIDTH);
var barX = canvasWidth - barSize;
var barY = BAR_HEIGHT * (i + 1);
ctx.fillStyle = 'rgba(10, 10, 10, .3)';
ctx.fillRect(barX - 10, barY, barSize + 10, CELL_WIDTH + 10);
ctx.fillStyle = player.baseColor.rgbString();
ctx.fillRect(barX, barY, barSize, CELL_WIDTH);
ctx.fillStyle = player.shadowColor.rgbString();
ctx.fillRect(barX, barY + CELL_WIDTH, barSize, SHADOW_OFFSET);
ctx.fillStyle = "black";
ctx.fillText(name, barX - nameWidth - 5, barY + CELL_WIDTH - 10);
}
}
//TODO: depict leaderboard.
@ -966,7 +981,7 @@ function paint(ctx)
paintGrid(ctx);
players.forEach(function (p) {
var fr = newPlayerFrames[p.num] || 0;
var fr = p.waitLag;
if (fr < ANIMATE_FRAMES)
p.render(ctx, fr / ANIMATE_FRAMES);
else
@ -1035,11 +1050,11 @@ function update(frame) {
portionSpeed = Math.abs(userPortion - lagPortion) / ANIMATE_FRAMES;
var dead = [];
core.updateFrame(grid, players, newPlayerFrames, dead, function addKill(killer, other)
core.updateFrame(grid, players, dead, function addKill(killer, other)
{
if (players[killer] === user && killer !== other)
kills++;
}, frame);
});
dead.forEach(function(val) {
console.log(val.name + " is dead");
allPlayers[val.num] = undefined;
@ -1051,15 +1066,6 @@ function update(frame) {
}
//Helper methods.
function modRotate(val, mod)
{
var res = val % mod;
if (res >= 0)
return res;
else
return mod + res;
}
function centerOnPlayer(player, pos)
{
var xOff = Math.floor(player.posX - (gameWidth / zoom - CELL_WIDTH) / 2);
@ -1101,7 +1107,6 @@ module.exports = exports = {
if (allPlayers[player.num])
return; //Already added.
allPlayers[player.num] = players[players.length] = player;
newPlayerFrames[player.num] = 0;
playerPortion[player.num] = 0;
return players.length - 1;
},
@ -8975,6 +8980,7 @@ var consts = require("./game-consts.js");
var GRID_SIZE = consts.GRID_SIZE;
var CELL_WIDTH = consts.CELL_WIDTH;
var NEW_PLAYER_LAG = 60; //wait for a second at least.
function defineGetter(getter) {
return {
@ -9261,13 +9267,17 @@ function fillTail(data)
function floodFill(data, grid, row, col, been)
{
var coords = [];
var filled = new Stack(40000);
var surrounded = true;
function onTail(c) { return data.tailGrid[c[0]] && data.tailGrid[c[0]][c[1]]; }
coords.push([row, col]);
var start = [row, col];
if (grid.isOutOfBounds(r, c) || been.get(row, col) || onTail(start) || grid.get(row, col) === data.player)
return; //Avoid allocating too many resources.
var coords = [];
var filled = new Stack(GRID_SIZE * GRID_SIZE + 1);
var surrounded = true;
coords.push(start);
while (coords.length > 0)
{
var coord = coords.shift();
@ -9327,6 +9337,7 @@ function Player(isClient, grid, sdata) {
data.posX = sdata.posX;
data.posY = sdata.posY;
this.heading = data.currentHeading = sdata.currentHeading; //0 is up, 1 is right, 2 is down, 3 is left.
data.waitLag = sdata.waitLag || 0;
data.dead = false;
//Only need colors for client side.
@ -9356,7 +9367,7 @@ function Player(isClient, grid, sdata) {
//Instance methods.
this.move = move.bind(this, data);
this.die = function() { if (data.num !== 1) /* GHOST PLAYER */ data.dead = true;};
this.die = function() { data.dead = true;};
this.serialData = function() {
return {
num: data.num,
@ -9364,12 +9375,13 @@ function Player(isClient, grid, sdata) {
posX: data.posX,
posY: data.posY,
currentHeading: data.currentHeading,
tail: data.tail.serialData()
tail: data.tail.serialData(),
waitLag: data.waitLag
};
};
//Read-only Properties.
defineAccessorProperties(this, data, "currentHeading", "dead", "name", "num", "posX", "posY", "grid", "tail");
defineAccessorProperties(this, data, "currentHeading", "dead", "name", "num", "posX", "posY", "grid", "tail", "waitLag");
Object.defineProperties(this, {
row: defineGetter(function() { return calcRow(data); }),
col: defineGetter(function() { return calcCol(data); })
@ -9423,12 +9435,15 @@ Player.prototype.render = function(ctx, fade)
};
function move(data, frame)
function move(data)
{
//console.log("P" + this.num + ": " + frame + ": " + this.heading);
if (data.waitLag < NEW_PLAYER_LAG)
{
data.waitLag++;
return;
}
//Move to new position.
var prevX = this.posX, prevY = this.posY;
var heading = this.heading;
if (this.posX % CELL_WIDTH !== 0 || this.posY % CELL_WIDTH !== 0)
heading = data.currentHeading;
@ -9446,12 +9461,6 @@ function move(data, frame)
var row = this.row, col = this.col;
if (data.grid.isOutOfBounds(row, col))
{
if (data.num === 1) /* GHOST PLAYER */
{
prevX = this.posX;
prevY = this.posY;
return;
}
data.dead = true;
return;
}

View File

@ -3,10 +3,11 @@ var http = require('http');
var serveStatic = require('serve-static');
// Serve up public/ftp folder
var serve = serveStatic('public/', {'cacheControl': false, 'setHeaders': setHeaders});
var serve = serveStatic('public/', {'setHeaders': setHeaders});
function setHeaders(res, path) {
res.setHeader("Access-Control-Allow-Origin", "http://paper-io-thekidofarcrania.c9users.io:8081");
res.setHeader('Cache-Control', 'public, max-age=0');
}
// Create server