Reputation: 167
I have designed an HTML5 Game with a square that shoots at other squares. You have a certain amount of lives and gain a score. How do I prevent users from going into the console and doing something like this:
score=5000
planetHealth=200
Code to Game
$(document).ready(function() {
initStars(600);
});
var FPS = 60;
width = 300;
height = 400;
var gBackground = document.getElementById("canvas_background").getContext("2d");
var gPlayer = document.getElementById("canvas_player").getContext("2d");
var gEnemies = document.getElementById("canvas_enemies").getContext("2d");
var GUI = document.getElementById("canvas_ui").getContext("2d");
var bullets = [];
var enemies = [];
var shootTimer = 0;
var maxShootTimer = 15;
var score = 0;
var planetHealth = 50;
var gameState = "menu";
var Key = {
up: false,
down: false,
left: false,
right: false
};
var player = {
width: 16,
height: 16,
x: (width / 2) - 8,
speed: 3,
y: height - 20,
canShoot: true,
render: function() {
gPlayer.fillStyle="#24430A";
gPlayer.fillRect(this.x,this.y,this.width,this.height);
},
tick: function() {
if(Key.left && this.x > 0) this.x -= this.speed;
if(Key.right && this.x < width - 20) this.x += this.speed;
if(Key.space && this.canShoot) {
this.canShoot = false;
bullets.push(new Bullet(this.x,this.y - 4));
bullets.push(new Bullet(this.x + this.width,this.y - 4));
shootTimer = maxShootTimer;
}
}
};
stars = [];
addEventListener("keydown", function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.which;
switch(keyCode) {
case 38: // up
Key.up = true;
break;
case 40: // down
Key.down = true;
break;
case 37: // left
Key.left = true;
break;
case 39: // right
Key.right = true;
break;
case 32: //spacebar
Key.space = true;
break;
}
}, false);
addEventListener("keyup", function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.which;
switch(keyCode) {
case 38: // up
Key.up = false;
break;
case 40: // down
Key.down = false;
break;
case 37: // left
Key.left = false;
break;
case 39: // right
Key.right = false;
break;
case 32: //spacebar
Key.space = false;
break;
}
}, false);
function collision(obj1,obj2) {
return (
obj1.x < obj2.x+obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y+obj2.height &&
obj1.y + obj1.height > obj2.y
);
}
function Star(x,y) {
this.x = x;
this.y = y;
this.size = Math.random() * 2.5;
this.render = function() {
gBackground.fillStyle = "white";
gBackground.fillRect(this.x,this.y,this.size,this.size)
};
this.tick = function() {
this.y++;
}
}
function createStars(amount) {
for(i=0;i<amount;i ++) {
stars.push(new Star(Math.random() * width, -5));
}
}
function initStars(amount) {
for(i=0;i<amount;i++) {
stars.push(new Star(Math.random()*width,Math.random()*height));
}
}
function Bullet(x,y) {
this.x = x;
this.y = y;
this.width = 2;
this.height = 12;
this.speed = 3;
this.render = function() {
gPlayer.fillStyle = "red";
gPlayer.fillRect(this.x,this.y,this.width,this.height);
};
this.tick = function() {
if(this.y < -this.height) {
var index = bullets.indexOf(this);
bullets.splice(index,1);
}
this.y-=this.speed;
for(i in enemies) {
if(collision(this,enemies[i])) {
score = score + 50;
GUI.clearRect(0,0,width,height);
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
var enemyIndex = enemies.indexOf(enemies[i]);
enemies.splice(enemyIndex,1);
var bulletIndex = bullets.indexOf(this);
bullets.splice(bulletIndex,1);
}
}
};
}
function Enemy(x,y) {
this.x = x;
this.y = y;
this.width = 16;
this.height = 16;
this.speed = 0.5;
;
this.render = function() {
gEnemies.fillStyle = "red";
gEnemies.fillRect(this.x,this.y,this.width,this.height);
};
this.tick = function() {
if(this.y > this.height + height) {
this.y = -this.height;
planetHealth--;
GUI.clearRect(0,0,width,height);
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
}
this.y += this.speed;
}
}
for(x=0;x<8;x++) {
for(y=0;y<8;y++) {
enemies.push(new Enemy((x*24)+(width/2)-100,y*24));
}
}
function render() {
if(gameState == "play") {
gBackground.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
gEnemies.clearRect(0,0,width,height);
player.render();
for(i in stars) {
stars[i].render();
}
for(i in enemies) enemies[i].render();
for(i in bullets) bullets[i].render();
} else if(gameState == "gameOver") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("You're a loser!", width / 2 - 100, height/2);
gEnemies.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
} else if(gameState == "gameWin") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("You're a winner!", width / 2 - 100, height/2);
gEnemies.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
} else if(gameState == "menu") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("Space Game!", width / 2 - 100, height/2);
GUI.font= "normal 16px Tahoma";
GUI.fillText("Press space to start", width / 2 - 90, (height/2)+28);
}
}
if(gameState == "play") {
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
}
function tick() {
createStars(1);
for(i in stars) stars[i].tick();
if(gameState == "play") {
if(planetHealth <= 0) gameState = "gameOver";
if(enemies.length <= 0) gameState = "gameWin";
player.tick();
for(i in enemies) enemies[i].tick();
for(i in bullets) bullets[i].tick();
if(shootTimer <= 0) player.canShoot = true;
shootTimer--;
} else if(gameState == "menu") {
if(Key.space) {
gameState = "play";
GUI.clearRect(0,0,width,height);
}
}
}
setInterval(function() {
render();
tick();
}, 1000/FPS );
<!DOCTYPE html>
<html>
<head>
<title> Game </title>
<style>
canvas {
position: absolute;
top: 0;
left: 0;
}
#canvas_background {
background: black;
}
</style>
</head>
<body>
<canvas id='canvas_background' width='300' height='400'></canvas>
<canvas id='canvas_player' width='300' height='400'></canvas>
<canvas id='canvas_enemies' width='300' height='400'></canvas>
<canvas id='canvas_ui' width='300' height='400'></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src='game.js'></script>
</body>
</html>
Upvotes: 2
Views: 1607
Reputation: 119847
You can't prevent a user from tampering with the console and the browser's dev tools gives you a lot of ways to take a peek anywhere in the code, even in closures as well as minified code.
But... you could make it harder.
First, you could do it like Facebook and just print a big red warning in the console saying "You shouldn't be here". We're essentially just scaring off the user, really.
Another option is to wrap the code in a closure, that way it's not exposed in the global scope. This avoids direct manipulation via the console.
;(function(){
// all code in here
());
Making it a bit harder is to use a minifier and an obfuscator.
The main purpose of a minifier is to shave file size by renaming names and rewriting code in a shorter way. The side effect is that the code becomes hard to read as most of the time it won't have any resemblance to your original code. It's worse without a source map and may take hours to trace and understand.
An obfuscator rewrites your code in a way that it still runs the same, just written in a different and often non-readable way. They even go as far as encoding the rewritten code in base64. For those who don't know what base64 is, they're good as gone.
Again, we're just making your code a bit harder to reach, fending off wannabe "hackers".
A more fool-proof way would be to just validate off-page, like on the server and use a variety of methods to determine tampered data. Games like speed typing impose a max score at a certain length of time since we all know we can't type a million words a second. Some games involve data analysis, if the data looks out of the ordinary.
Upvotes: 8
Reputation: 1516
You're running your code on the main scope of the Javascript, which is window
.
When you create a global varable, this variable is scoped in the window
object.
For that reason you can do this:
var a = 1;
console.log(a); // 1
console.log(window.a); // 1
It's very easy to avoid this, using the famous IIFE, which stands for Immediately Invoked Function Expression. You can read it at MDN.
Is just do this:
(function() {
// put all your code inside here
})();
When you do this, all the variable decladed inside that function, will be contained to the scope of that function.
But be aware, you can't prevent user cheating the game, you can only make it harder.
Upvotes: 0
Reputation: 1405
You don't. If your game is entirely client-side, then you can't really stop players from cheating. You could make it more difficult with code obfuscation or taking variables out of the global scope, but that won't stop people who really want to cheat.
If players are connecting to a server for multiplayer or whatever you could implement server-side checks, since the users won't be able to touch that code.
Upvotes: 1