I've written a game in javascript and canvas. It works fine. But i want to add a high scores database to it. I have made mysql database and use php to access the database and create an array that is passed back to the javascript. When a user score that is greater than any of the high scores i would like to allow the user to enter there name and it store the name and score in the database. I could do that easily but the problem is then anyone could navigate to the enter high scores page and send a random high score. What i can't get my head around is how to verify that the user did actually get that score. Since the users score is only really handled by the javascript. I can't for the life of me work out how i am meant to verify that the score is a real high score.
Code:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body{
margin:0;
padding:0;
}
</style>
<title>Hidden Objects</title>
<script type="text/javascript">
// initialise gloabl variables
var images = new Array();
var positions_x = new Array();
var positions_y = new Array();
var selected_imgs = new Array();
var correct_imgs = new Array();
var high_scores = new Array();
var img = new Array();
var status = new Array();
var round = 1; //3 rounds per level 5 images per round
var corrects = 0;
var timer_total = 90;
var timer = timer_total;
var level = 1;
var score = 0;
for(i=0;i<50;i++)
{
status[i] = 1;
}
<?php
//localhost:8888/hidden-objects/
// read objects images
$i=0;
$images = array();
if ($handle = opendir('images')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo "images[$i] ='$entry';<br />\n";
$images[] = $entry;
$i++;
}
}
closedir($handle);
}
//get high scores from the database
$link = mysql_connect('localhost', 'root', 'root');
if($link)
{
mysql_select_db("HIDDEN_OBJECTS");
}
else
die("Could not connect to databse");
$sql = "SELECT * FROM HIGH_SCORES ORDER BY SCORE DESC";
if($res = mysql_query($sql))
{
$i=0;
while($row = mysql_fetch_assoc($res))
{
echo "
high_scores[$i] = new Array(2);
high_scores[$i][0] = '".$row["NAME"]."';
high_scores[$i][1] = ".$row["SCORE"].";
";
$i++;
}
}
?>
function init() {
// iniitialise the canvas and start main menu interval.
can = document.getElementById("can");
<?php
foreach($images as $key=>$image)
{
echo "img[$key] = document.getElementById('image_$key');
";
}
?>
level_bkdrop1 = document.getElementById('level_bkdrop1');
level_bkdrop2 = document.getElementById('level_bkdrop2');
level_bkdrop3 = document.getElementById('level_bkdrop3');
level_bkdrop4 = document.getElementById('level_bkdrop4');
level_bkdrop5 = document.getElementById('level_bkdrop5');
applause = document.getElementById("applause");
bloop = document.getElementById("bloop");
blurp = document.getElementById("blurp");
boo = document.getElementById("boo");
ctx =can.getContext("2d");
ctx.fillRect(0,0,665,500);
ctx.strokeStyle="white";
alpha=0;
main_menu_timer = setInterval(main_menu, 5);
}
function main_menu()
{
//main menu at start
if(alpha<1)
alpha+=.1;
ctx.globalAlpha = alpha;
ctx.font = "bold 36px sans-serif";
ctx.shadowColor = "rgb(100, 100, 25)";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillText("Start Game", 250, 200);
ctx.fillText("High Scores", 250, 250);
// get the mouse co ords
document.onmousemove = function( ev ){
x_mouse_pos = ev.pageX;
y_mouse_pos = ev.pageY;
};
// if mouse clicked and co ords with in start game button start game
document.onclick = function( ev ){
if(x_mouse_pos > 250 && x_mouse_pos < 450)
{
if(y_mouse_pos > 170 && y_mouse_pos < 200)
{
clearInterval(main_menu_timer);
alpha = 0;
ctx.fillStyle = "black";
cls_timer = setInterval(clear_screen, 50);
}
}
if(x_mouse_pos > 250 && x_mouse_pos < 460)
{
if(y_mouse_pos > 220 && y_mouse_pos < 250)
{
clearInterval(main_menu_timer);
alpha = 0;
ctx.fillStyle = "black";
ctx.fillRect(0,0,1100,500);
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "rgb(200, 200, 50)";
high_timer = setInterval(high_scores_func, 50);
}
}
};
}
function new_score()
{
// add input for a new high score
//what do i do here?
}
function high_scores_func()
{
if(alpha<1)
alpha+=.1;
ctx.globalAlpha = alpha;
ctx.font = "bold 24px sans-serif";
for(i=0;i<10;i++)
{
ctx.fillText(high_scores[i][0]+" : "+high_scores[i][1], 170, 40+(i*40));
}
ctx.font = "bold 36px sans-serif";
ctx.fillText("Main Menu", 400, 450);
document.onmousemove = function( ev ){
x_mouse_pos = ev.pageX;
y_mouse_pos = ev.pageY;
};
document.onclick = function( ev ){
if(x_mouse_pos > 400 && x_mouse_pos < 620)
{
if(y_mouse_pos > 420 && y_mouse_pos < 450)
{
clearInterval(high_timer);
alpha = 0;
ctx.fillStyle="black";
ctx.fillRect(0,0,665,500);
main_menu_timer = setInterval(main_menu, 5);
}
}
};
}
function clear_screen()
{
// clears the screen on game start and enter main loop
ctx.globalAlpha = alpha;
alpha +=0.1;
ctx.fillRect(0,0,1100,500);
if(alpha>=1)
{
clearInterval(cls_timer);
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.globalAlpha = 1;
setup_game();
game_timer = setInterval(main_loop, 1);
}
}
function clear_screen2()
{
//clear the screen on game over and enter back to main menu loop
ctx.globalAlpha = alpha;
alpha +=0.1;
ctx.fillStyle="black";
ctx.fillRect(0,0,1100,500);
ctx.shadowColor = "rgb(100, 100, 25)";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillText("Start Game", 250, 200);
if(alpha>=1)
{
clearInterval(cls2_timer);
main_menu_timer = setInterval(main_menu, 5);
}
}
function is_too_close(x, y, total)
{
// checks if the objects are too close to each other
for(j=0;j<total;j++)
{
if(positions_x[j] - 4 < x && positions_x[j] +4 > x && positions_y[j] - 4 < y && positions_y[j] +4 > y )
return true;
}
return false;
}
function setup_game()
{
// sets up some of the game variables. runs each time a new level is reached.
start = new Date().getTime();
start = Math.floor(start/1000);
//set up random positions for all the objects
for(i=0;i<50;i++)
{
do
{
positions_x[i] = Math.floor((Math.random()*380)+221);
positions_y[i] = Math.floor((Math.random()*440)+11);
}while(is_too_close(positions_x[i], positions_y[i], i))
}
// pick 15 objects out at random... these are the objects the user has to find
for(i=0;i<15;i++)
{
rand = Math.floor(Math.random()*49);
while(in_array(rand, selected_imgs))
{
rand = Math.floor(Math.random()*49)
}
selected_imgs[i] = rand;
correct_imgs[i]=1;
}
}
function next_level()
{
// This funtion is run when a new level reached
//first check if finished game
if(level==6 && nxt_lvl_timer==0)
{
//end game
step = 3;
}
switch(step)
{
//step 1 - clear screen and display the level they are to start
case 1:
ctx.globalAlpha = alpha;
if(alpha<1)
alpha +=0.1;
ctx.fillStyle="black";
ctx.fillRect(0,0,1100,500);
ctx.shadowColor = "rgb(100, 100, 25)";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillText("Level "+(level+1), 250, 200);
ctx.fillText("Time Bonus: "+ (timer*100), 170, 250);
nxt_lvl_timer++;
if(nxt_lvl_timer>=100)
{
step = 2;
alpha=0;
}
break;
case 2:
// step 2 - clear screen and setup game for new level
ctx.globalAlpha = alpha;
if(alpha<1)
alpha +=0.1;
ctx.fillStyle="black";
ctx.fillRect(0,0,1100,500);
nxt_lvl_timer++;
if(nxt_lvl_timer>=130)
{
step = 0;
clearInterval(next_level_timer);
timer_total-=10;
timer = timer_total;
corrects = 0;
round=1;
level++;
for(i=0;i<50;i++)
{
status[i] = 1;
}
setup_game();
game_timer = setInterval(main_loop, 1);
}
break;
case 3:
ctx.globalAlpha = alpha;
if(alpha<1)
alpha +=0.1;
ctx.fillStyle="black";
ctx.fillRect(0,0,1100,500);
ctx.shadowColor = "rgb(100, 100, 25)";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillText("Completed Game", 170, 200);
ctx.fillText("Time Bonus: "+ (timer*100), 170, 250);
ctx.fillText("Game Bonus: "+ 10000, 170, 300);
ctx.fillText("Your score: "+(score+10000+(timer*100)),170, 350);
nxt_lvl_timer++;
if(nxt_lvl_timer>=100)
{
clearInterval(next_level_timer);
score +=10000;
score +=timer*100;
alpha=0;
level=1;
corrects=0
timer_total = timer = 90;
round=1;
for(i=0;i<50;i++)
{
status[i] = 1;
}
if(check_for_high_score())
{
high_timer2 = setInterval(new_score, 5);
}
else
cls2_timer = setInterval(clear_screen2, 5);
}
break;
}
}
function check_for_high_score()
{
for(i=0;i<10;i++)
{
if(score> high_scores[i][1])
{
return true;
}
}
return false;
}
function game_over()
{
// game over - clear screen and show game over text
ctx.globalAlpha = alpha;
if(alpha<1)
alpha +=0.1;
ctx.fillStyle = "black";
score = 0;
ctx.fillRect(0,0,1100,500);
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillText("Game Over", 250, 200);
end_game_timer++;
if(end_game_timer>=30)
{
clearInterval(game_over_timer);
round = 1; //3 rounds per level 5 images per round
corrects = 0;
timer_total = timer = 90;
level=1;
for(i=0;i<50;i++)
{
status[i] = 1;
}
if(check_for_high_score())
{
high_timer2 = setInterval(new_score, 5);
}
else
cls2_timer = setInterval(clear_screen2, 5);
}
}
function main_loop()
{
// main game loop
// get the current time
now = new Date().getTime();
now = Math.floor(now /1000);
// test if level completed. Id so go to next_level function
if(round==4)
{
clearInterval(game_timer);
applause.play();
alpha=0;
step=1;
score += timer*100;
nxt_lvl_timer = 0;
next_level_timer = setInterval(next_level, 50);
}
// if time runs out game over
if(timer<0)
{
clearInterval(game_timer);
boo.play();
alpha=0;
end_game_timer=0;
game_over_timer = setInterval(game_over, 50);
}
draw_template();
draw_timer();
draw_imgs();
// get mouse postition
document.onmousemove = function( ev ){
x_mouse_pos = ev.pageX;
y_mouse_pos = ev.pageY;
};
// if mouse clicked loop through objects
found = false;
document.onclick = function( ev ){
clearInterval(game_timer);
for(i=49;i>=0;i--)
{
// test if mouse clicked over and object
if(x_mouse_pos > positions_x[i] && x_mouse_pos< positions_x[i]+img[i].width)
{
if(y_mouse_pos > positions_y[i] && y_mouse_pos < positions_y[i]+img[i].height)
{
// test if mouse clicked on an object in this round
for(j=((round*5)-5);j<(round*5);j++)
{
if(selected_imgs[j]==i && found==false && correct_imgs[j]==1)
{
bloop.play();
status[i] = 2;
correct_imgs[j] = 0;
found = true;
/*
// debugging code example
document.getElementById("debug").innerHTML=document.getElementById("debug").innerHTML+
"found "+i+" - "+img[i].src+"<br />";
*/
score+=100+((level-1)*20);
corrects++;
if(corrects==5)
{
corrects = 0;
round++;
}
}
}
}
}
}
// 5 seconds penalty
if(!found)
{
blurp.play();
start = start -5;
}
game_timer = setInterval(main_loop, 1);
}
}
function draw_timer()
{
// draw the timer at the bottom of screen and the current level and score
timer = timer_total-(now - start);
ctx.strokeStyle="red";
ctx.strokeText(timer + " Level - " +level , 15, 440);
ctx.font = "bold 14px sans-serif";
ctx.strokeText("Score: " +score , 15, 470);
ctx.font = "bold 36px sans-serif";
}
function draw_imgs()
{
// draw the objects
j=0;
for(i=((round*5)-5);i<(round*5);i++)
{
if(correct_imgs[i]==1)
{
index = selected_imgs[i];
ctx.drawImage(img[index], 30, ((j*70)+30));
}
j++;
}
score_i_messages = new Array();
score_i_alpha = new Array();
for(i=0;i<50;i++)
{
if(status[i]==1)
ctx.drawImage(img[i], positions_x[i], positions_y[i]);
else
{
if(status[i]>1)
{
if(status[i]>30)
status[i]=0;
else
{
ctx.globalAlpha = 1-(status[i]/30);
ctx.drawImage(img[i], positions_x[i], positions_y[i]);
score_i_messages[i] = "+"+(100+((level-1)*20));
score_i_alpha[i] = 1-(status[i]/30);
status[i]++;
ctx.globalAlpha =1;
//
}
}
}
}
for(var key in score_i_messages)
{
var value = score_i_messages[key];
ctx.globalAlpha = score_i_alpha[key];
ctx.strokeText(score_i_messages[key], positions_x[key], positions_y[key]);
ctx.globalAlpha =1;
}
}
function draw_template()
{
// the the game template including level backdrop
ctx.fillStyle = "rgb(200, 200, 50)";
ctx.fillRect(0,0,665,500);
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle ="white";
ctx.fillRect(10,10,200,480);
switch(level)
{
case 1:
ctx.fillRect(220,10, 435, 480);
break;
case 2:
ctx.drawImage(level_bkdrop1 , 220,10);
break;
case 3:
ctx.drawImage(level_bkdrop2 , 220,10);
break;
case 4:
ctx.drawImage(level_bkdrop3 , 220,10);
break;
case 5:
ctx.drawImage(level_bkdrop4 , 220,10);
break;
case 6:
ctx.drawImage(level_bkdrop5 , 220,10);
break;
}
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
function in_array (needle, haystack, argStrict) {
// Checks if the given value exists in the array
//
// version: 1109.2015
// discuss at: http://phpjs.org/functions/in_array // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: vlado houba
// + input by: Billy
// + bugfixed by: Brett Zamir (http://brett-zamir.me)
// * example 1: in_array('van', ['Kevin', 'van', 'Zonneveld']); // * returns 1: true
// * example 2: in_array('vlado', {0: 'Kevin', vlado: 'van', 1: 'Zonneveld'});
// * returns 2: false
// * example 3: in_array(1, ['1', '2', '3']);
// * returns 3: true // * example 3: in_array(1, ['1', '2', '3'], false);
// * returns 3: true
// * example 4: in_array(1, ['1', '2', '3'], true);
// * returns 4: false
var key = '', strict = !! argStrict;
if (strict) {
for (key in haystack) {
if (haystack[key] === needle) { return true;
}
}
} else {
for (key in haystack) { if (haystack[key] == needle) {
return true;
}
}
}
return false;
}
</script>
</head>
<body onload="init()">
<?php
foreach($images as $key=>$image)
{
echo '<img id="image_'.$key.'" src="images/'.$image.'" style="display:none;">
';
}
?>
<img id="level_bkdrop1" src="backdrops/beach.jpg" style="display:none;">
<img id="level_bkdrop2" src="backdrops/bathroom.jpg" style="display:none;">
<img id="level_bkdrop3" src="backdrops/gallery.jpg" style="display:none;">
<img id="level_bkdrop4" src="backdrops/diningroom.jpg" style="display:none;">
<img id="level_bkdrop5" src="backdrops/clutter.jpg" style="display:none;">
<div id="debug" style="font-size:xx-small;float:right;clear:none;"></div>
<canvas id="can" height="500" width="665">
</canvas>
<audio id="applause" src="sounds/applause_y.wav">
<audio id="bloop" src="sounds/bloop_x.wav">
<audio id="blurp" src="sounds/blurp_x.wav">
<audio id="boo" src="sounds/boo.wav">
</body>
</html>
Its the new_score function is the only thing missing at the moment. I wondered if there is something i can do with ajax here? Or it a dead loss.
I wanted to upload the game to a zip with all the images and sounds but i cant attach it as its 512KB to big for the forum. I know it will be hard to test with out the images but if anyone can suggest anything. please do!
__________________
You can not say you know how to do something, until you can teach it to someone else.
I can't really think of any way to prevent it. Other than through some easy-to-defeat means that would only stop the casual cheater, I can't even think of any good way to widely deter it. Since the data comes from the user, they will always be able to manipulate it and abuse your system. If you want to stop the vast majority of cheating I think you probably need to go with a compiled language running through a browser plugin (Java, Flash, etc.).
My best guess for what to do with a pure js application is to encode the $_GET or $_POST string you send to the PHP page so it's not as simple as ?myscore=550. If you find a reversible way to encode it (or even encrypt it with a shared key - perhaps one that is generated randomly for each session but persists throughout a given session until that session sets a record, at which time the key changes - that way no two sessions would be likely to be able to use the same variable values to set a score and a user who set a score once wouldn't be able to resubmit the string to get a second high score) you could at least keep the totally uninitiated from cheating your system. If you obfuscate the variable names and functions and/or stuff the script inside an eval() you could make it one step more inconvenient to cheat. But nothing will ever be even clost to bullet-proof.
So the question is: "Is the effort worth the benefit?" That's up to you.
well im not too fussed really as the game was originally designed for my girlfriend as she likes hidden object games. But i had the idea of hosting it on the web so i can let other people play. Maybe i will use sessions and just see if it gets abused lol
__________________
You can not say you know how to do something, until you can teach it to someone else.