Ace100 # NotGood20 # # If you are returning any additional data (such as the "time" someone achieved # a certain score), that variable will have its own element tag name: # # Ace100 # # # If $return_type is set to HTML, output is usable for a regular web page. This # gives you a nice option of having the high scores table come up in its own # pop-up window. The HTML template that is used for this output type is located # toward the end of this script at "# HTML TEMPLATE START #". This code may be # customized to suit your needs. # #################### PART 4: DATABASE FILE FORMAT ###################### # Database file names will be based on 3 variables: game_title, game_author, # and $storage_type. If you have: # game_title = My Great Game # game_author = Kaptain Kory # $storage_type = PLAIN # The database file will be named: # My_Great_Game-Kaptai.txt # The name is trucated to a maximum of 20 characters long. This file will be # created in the folder specified by $data_folder when there is data to store. # If you change $storage_type to XML, the database will be renamed to: # My_Great_Game-Kaptai.xml # # If the variable game_title is not sent to the script, a database file called # Scores_Data.txt or Scores_Data.xml will be used. # # If $storage_type is set to PLAIN, records will be delimited with a "|": # # Jobe|1000 # Kory|800 # # Records will contain a minimum of 2 items, name and score. Data items # specified in $additional_data_stored will be appended to each record: # # Troy|1000|12:00pm|Great game here.|MO # Steve|800|4:00pm|Cool effects!|AR # # If $storage_type is set to XML, records will be stored in an XML formatted # file: # # # # Ace # 100 # # # # NotGood # 20 # # # # # This XML file indicates that an additional variable, "time", has been set in # $additional_data_stored. # ######################## PART 5: FINDING HELP ########################## # 1) Read the instructions file that comes with this script. # 2) Look on the website for links to tutorials and other downloads. # 3) Read the documentation that comes on your server for details on how to # install a PHP script, or ask your web administrator. # 4) Look carefully at the examples which come with this script. # 5) Print the top portion of this script so that you have a ready reference to # the help available here. In the text editor I am using, this would be the # first 8 pages. # 6) Post a message for assistance on a Flash user board or in the Macromedia # Flash newsgroup. # 7) Email me for help. # ############################ PART 6: PHP ############################### # This script will NOT simply run on your operating system. It must be installed # on a server. # # Can you even run PHP scripts on your server? Check with your web # administrator if unsure. # # Upload this file to your server in ASCII mode then CHMOD this file to 0755. # # For troubleshooting purposes, you may try uncommenting the following 2 lines # and calling this script from a browser. You will get a whole lot of # information about how PHP is set up on your server. # # phpinfo(); # exit; # ########################## PART 7: FLASH/PHP ########################### # The Flash movie and this script must be located on the same server. So, # testing your movie from Flash and calling the script on your server (even with # an absolute path) will not work! This is a detail that is missed by a lot of # people. # # In Flash, only those variables declared in the same level as the loadVariables # action will be sent via POST or GET. This means that if you don't want to send # a whole bunch of useless variables to this script, you should probably place # the loadVariables action in a separate MovieClip. If you need to get variables # from the main timeline, you could do something like: # var my_name = _root.name; # var my_score = _root.score; # # Similar to above, variables returned by this script will only be visible in # the target MovieClip or Level that is specified in the loadVariables action. # If a target or Level is not specified, the variables will be visible on the # same level (i.e. path) as the loadVariables call. # # Use getURL rather than loadVariables if you have set $return_type to HTML. # # Use XML.load (NOT XMLSocket) if you have set $return_type to XML. # ########################## PART 8: FLASH/XML ########################### # First of all, the XML parsing used in this script is NOT general purpose. If # you play around with adding attributes, other elements, or change the # formatting of the database, this could affect the script's ability to properly # parse the document. # # Flash has several idiosyncrasies in regard to using XML. If you don't believe # me, just visit your favorite Flash user board or news group. Before you get # gung-ho about using XML, please make sure you familiarize yourself with the # potential pitfalls involved. # # What you will likely find if you have data returned to your Flash movie in XML # format: # a) Reduced performance of your Flash movie while it is parsing the XML # data. This may even lock up your movie/browser/operating system for # a couple of seconds. # b) Greatly reduced performance as your database grows in size. # c) Difficulties writing the ActionScript to handle the XML data. # # Ok, now that I've said that, it may still be useful for you to store your data # in XML format. Here are just a few reasons why XML might be the way to go: # a) Readability. It is definitely easier to "read" an XML file than it # is the plain-text format. # b) Fun in Flash. Okay, I'll admit it. Playing around with XML data in # Flash can be fun (for some). But it can also be quite tricky. # c) Linkage to CSS. Although this would require some minor adjustments # to this script, you might like to create a nicely formatted XHTML # file of the data. # d) Extraction of specific data. For example, let's imagine that you # have set "emails" as a value in $additional_data_stored . You may # find it easier to write a script that will extract data from the # "email" tags in XML, than it would be to parse that information from # the plain-text format. # e) mySQL, PostgreSQL, or other real database (as opposed to plain-text # database). If you plan to extend this script for using a real # database, you may find it easier to use XML as an intermediate # format. There are several Perl and PHP modules and scripts already # written for parsing XML and also for using databases. Now you have # used the *temptation* of posting a score for your game to collect # valuable user information. # f) Prewritten scripts in Perl, PHP, ASP, etc. There are probably # numerous scripts already written for dealing with XML data in fun and # interesting ways. HotScripts (http://www.hotscripts.com) maintains a # nice selection of scripts. # # This script makes the switch from PLAIN to XML as needed. This means that you # can change $storage_type and $return_type to whatever you need as many # different times as you need without losing data. This cannot, however, be # said for $additional_data_stored. It is not advisable to change this variable # once a database has been established (or at least back-up your database before # you attempt to change it). # ################### PART 9: BACKWARDS COMPATIBILITY #################### # This script is backwards compatible with ScoreKeeper 2.1, 2.0 (and probably # even 1.0). # # You do not need to reformat or change your database files in any way. Nor do # you need to change your Flash movies to be compatible with this new version # (except possibly to change the name of this script). # # For use with Flash 4, set $return_type to either PLAIN or HTML. ################################################################################ ################################################################################ # DO NOT ALTER THE SCRIPT BEYOND THIS POINT! (Unless you really know what you # are doing and can clearly document any changes you make. This is ONLY if you # can program in PHP.) # # For those of you who would like to modify this script, I have tried to keep # the code cleaned up and commented. Since it is likely your customization # attempts will involve altering the HTML or XML handling, you should begin at # the subroutine scores_output. You might also like to look at xml_parse_input # and xml_parse_output. Use the Find on your text editor to jump right to those # places. # # Please remember to document any changes you make to this script. ################################################################################ # Basic logic: # PRESECURITY. presecurity(); # SECURITY CHECK. security_check(); # PREPROCESS. preprocess(); # READ scores from database file. scores_read(); # PROCESS scores. scores_process(); # SAVE scores to database file. scores_save(); # OUTPUT results. scores_output(); # WHEW! FINISHED! exit; # Everything else is details, details, boring details... function presecurity() { global $HTTP_GET_VARS, $HTTP_POST_VARS; global $game_title, $game_author, $my_name, $my_score; global $additional_data_stored, $additional_data_returned, $additional_data_hash; global $return_type, $scores_to_return, $scores_per_person, $anonymous_name, $sort_scores_in_reverse; global $data_folder, $folder_chmod, $session_id_folder; # gather all parameters from POST and GET (POST overrides GET) foreach ($HTTP_GET_VARS as $key => $value) { $param[$key] = $value; } foreach ($HTTP_POST_VARS as $key => $value) { $param[$key] = $value; } # gather environmental variables for "additional data" $param['user_agent'] = getenv('HTTP_USER_AGENT'); $param['user_host'] = getenv('REMOTE_HOST'); $param['user_ip'] = getenv('REMOTE_ADDR'); # get initial values if (isset($param['game_title'])) { $game_title = $param['game_title']; } if (isset($param['game_author'])) { $game_author = $param['game_author']; } if (isset($param['my_name'])) { $my_name = $param['my_name']; } if (isset($param['my_score'])) { $my_score = $param['my_score']; } # get initial values for additional data foreach ($additional_data_stored as $key) { if (! $key) { break; }; if (isset($param[$key])) { $additional_data_hash[$key] = $param[$key]; } else { $additional_data_hash[$key] = ""; } } # check for on-the-fly variables ('return_to_flash' is for backwards compatability) if (isset($param['return_type'])) { if (preg_match('/plain/i', $param['return_type']) or preg_match('/true/i', $param['return_to_flash'])) { $return_type = "PLAIN"; } else if (preg_match('/html/i', $param['return_type']) or preg_match('/false/i', $param['return_to_flash'])) { $return_type = "HTML"; } else if (preg_match('/xml/i', $param['return_type'])) { $return_type = "XML"; } } if (isset($param['scores_to_return']) and preg_match('/^\d+$/', $param['scores_to_return'])) { $scores_to_return = $param['scores_to_return']; } if (isset($param['scores_per_person']) and preg_match('/^\d+$/', $param['scores_per_person']) and $param['scores_per_person'] <= $scores_per_person) { $scores_per_person = $param['scores_per_person']; } if (isset($param['anonymous_name'])) { $anonymous_name = $param['anonymous_name']; } if (isset($param['sort_scores_in_reverse']) and preg_match('/^true|false$/i', $param['sort_scores_in_reverse'])) { $sort_scores_in_reverse = $param['sort_scores_in_reverse']; } # figure out the data folder and session folder locations if (! getenv('SCRIPT_FILENAME') or preg_match('/php\.exe$/i', getenv('SCRIPT_FILENAME'))){ $path = getenv('PATH_TRANSLATED'); } else { $path = getenv('SCRIPT_FILENAME'); } #$path = preg_replace('/(.*[\/\\\]).*$/', '\\1', $path); # if the above is creating problems, uncomment and edit the next line $path = '/home/ftpvirt/oddtodd/oddtodd.com/'; if ($data_folder == "") { $data_folder = 'data'; } $data_folder = $path . $data_folder; # if the data folder does not exist, create it if (! @is_dir($data_folder)) { mkdir($data_folder, $folder_chmod) or error("Could not make directory at " . __LINE__ . "."); } # session folder location $session_id_folder = $path . $session_id_folder; } function security_check() { global $HTTP_COOKIE_VARS; global $game_title, $game_author, $my_name, $my_score; global $additional_data_stored, $additional_data_returned, $additional_data_hash; global $limit_to_post, $limit_to_cookies, $limit_referrers, $require_session_id, $session_id_folder; global $error_message_on_security_failure; $security_check = "passed"; # security check for limiting to POST if (! preg_match('/post/i', getenv('REQUEST_METHOD')) and preg_match('/true/i', $limit_to_post)) { $security_check = "failed"; } # security check for limiting to cookies if (preg_match('/true/i', $limit_to_cookies)) { if ($HTTP_COOKIE_VARS['game_title']) { $game_title = $HTTP_COOKIE_VARS['game_title']; setcookie('game_title'); } if ($HTTP_COOKIE_VARS['game_author']) { $game_author = $HTTP_COOKIE_VARS['game_author']; setcookie('game_author'); } if ($HTTP_COOKIE_VARS['my_name']) { $my_name = $HTTP_COOKIE_VARS['my_name']; setcookie('my_name'); } # my_score variable MUST come from cookie $my_score = $HTTP_COOKIE_VARS['my_score']; setcookie('my_score'); foreach ($additional_data_stored as $key) { if (! $key) { break; }; if ($HTTP_COOKIE_VARS[$key]) { $additional_data_hash[$key] = $HTTP_COOKIE_VARS[$key]; setcookie($key); } } if ($my_score == "") { $security_check = "failed"; }; } # security check for limiting to specific referrers for ($i=0; $i $value) { $additional_data_hash[$key] = preg_replace('/\|/', '/', $value); } } # was my_score sent to the script and is it a valid number? # pattern matches to any C float (from perlfaq4) if (! preg_match('/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/', $my_score)) { scores_read(); # no need to process or save here scores_output(); exit; } } function scores_read() { global $data_file, $scores_data; $buffer = ""; settype($scores_data, "array"); if (! file_exists($data_file)) { return 1; } $fh = fopen($data_file, "r") or error("Could not open database file for reading at line " . __LINE__ . "."); # lock the file while reading from it flock($fh, 2); while (! feof($fh)) { $buffer .= fgets($fh, 4096); } flock($fh, 3); fclose($fh); $scores_data = explode("\n", trim($buffer)); for ($i=0; $i.*?/i', $scores_data[0])) { # convert from XML format if necessary $scores_data = xml_parse_input($scores_data); } } function scores_process() { global $game_title, $game_author, $my_name, $my_score; global $additional_data_stored, $additional_data_returned, $additional_data_hash; global $scores_data, $new_record; global $sort_scores_in_reverse, $scores_per_person, $allow_duplicate_records; $additional_data_addon = ""; $count = 0; # get the additional data into a string foreach ($additional_data_stored as $key) { if (! $key) { break; }; $additional_data_addon .= '|' . $additional_data_hash[$key]; } # prepare new record $new_record = $my_name . '|' . $my_score . $additional_data_addon; # security check for allowing duplicate records (or rather NOT allowing...) if (preg_match('/false/i', $allow_duplicate_records)) { #look for match with other records foreach ($scores_data as $rec) { if ($new_record == $rec) { # no need to finish scores_process or save here scores_output(); exit; } } } # add on the new record array_unshift($scores_data, $new_record); for ($i=0; $i $scores_per_person) { array_splice($scores_data, $i, 1); } } } } # some sorting functions that have been added to the PHP version: function sort_default($a, $b) { if (isset($b)) { $tmp_a = explode('|', $a); $tmp_b = explode('|', $b); if ($tmp_a[2] == $tmp_b[2]) { return ($tmp_a[0] > $tmp_b[0]) ? 1 : -1; } return ($tmp_b[2] > $tmp_a[2]) ? 1 : -1; } } function sort_rev($a, $b) { if (isset($b)) { $tmp_a = explode('|', $a); $tmp_b = explode('|', $b); if ($tmp_a[2] == $tmp_b[2]) { return ($tmp_a[0] > $tmp_b[0]) ? 1 : -1; } return ($tmp_a[2] > $tmp_b[2]) ? 1 : -1; } } function scores_save() { global $data_file, $storage_type, $scores_to_store, $scores_data; $fh = fopen($data_file, "w") or error("Could not open database file for writing at line " . __LINE__ . "."); # lock the file while writing to it flock($fh, 2); if (preg_match('/plain/i', $storage_type)) { # save to PLAIN format for ($i=0; $i<=$scores_to_store-1; $i++) { if (! isset($scores_data[$i])) { break; }; fwrite($fh, $scores_data[$i]); fwrite($fh, "\r\n"); } } else { # else save to XML format fwrite($fh, xml_parse_output("store")); } flock($fh, 3); fclose($fh); #chmod($data_file,0766); } function scores_output() { global $return_type, $scores_data, $scores_to_return, $new_record; global $game_title, $game_author, $additional_data_returned; $marked = 0; # return header header("Expires: -1"); # time in past header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); if (preg_match('/xml/i', $return_type)) { header('Content-Type: text/xml; charset=ISO-8859-1'); } if (preg_match('/plain/i', $return_type)) { # PLAIN output print 'sk_done=no&'; for ($i=0; $i<$scores_to_return; $i++) { if (! isset($scores_data[$i])) { break; } $inc = $i+1; $record = explode('|', $scores_data[$i]); print 'name_' . $inc . '=' . rawurlencode($record[0]) . '&'; print 'score_' . $inc . '=' . rawurlencode($record[1]) . '&'; for ($j=0; $j'; } for ($i=0; $i<$scores_to_return; $i++) { if (! isset($scores_data[$i])) { break; }; $inc = $i+1; $scores_table .= "\n"; # mark the score the player got by coloring the row if ($scores_data[$i] == $new_record and $marked != 1) { $scores_table .= ''; $marked = 1; } else { $scores_table .= ''; } $record = explode('|', $scores_data[$i]); $scores_table .= '' . $inc . ''; $scores_table .= '' . htmlspecialchars($record[0]) . ' '; for ($j=0; $j'; } } $scores_table .= '' . htmlspecialchars($record[1]) . '  '; $scores_table .= ''; } if (! $scores_data[0]) { # no scores yet? $scores_table .= 'No high scores available!'; } $scores_table .= ''; # plug in information into the HTML template # HTML TEMPLATE START # print << HighScores for the (Bad) Memory Game
 $game_title High Scores www.oddtOdd.com
$scores_table

 Game and layout © 2002  Geoffrey Noles  www.ae4rv.com
 Concept and layout © 2002 No Nothing Productions www.oddtOdd.com

EOF; # HTML TEMPLATE END # } } function xml_parse_input($records_in) { global $additional_data_stored; # join the input data into a string, extract each record into a new array preg_match_all('/\(.*?)\<\/record\>/i', join("", $records_in), $xml_records); foreach ($xml_records[1] as $rec) { # extract the data for each tag in the record $name = preg_replace('/.*\(.*?)\<\/name\>.*/i', '\\2', $rec); $score = preg_replace('/.*\(.*?)\<\/score\>.*/i', '\\2', $rec); foreach ($additional_data_stored as $tag) { if (! $tag) { break; }; if (preg_match('/.*\<' . $tag . '(.*?)\>(.*?)\<\/' . $tag . '\>.*/i', $rec)) { $ad_data[] = preg_replace('/.*\<' . $tag . '(.*?)\>(.*?)\<\/' . $tag . '\>.*/i', '\\2', $rec); } } # add in the new delimited record settype($ad_data, "array"); $records[] = join('|', array($name, $score, join('|', $ad_data))); unset($ad_data); # decode special characters $records[sizeof($records)-1] = preg_replace('/>/', '>', $records[sizeof($records)-1]); $records[sizeof($records)-1] = preg_replace('/</', '<', $records[sizeof($records)-1]); } return $records; } function xml_parse_output($action) { global $scores_to_store, $scores_to_return; global $scores_data, $additional_data_stored, $additional_data_returned; $sp = ""; $br = ""; if ($action == "store") { # store XML data $sp = ' '; $br = "\n"; $num = $scores_to_store-1; $ad_data = $additional_data_stored; } else if ($action == "return") { # return XML data $num = $scores_to_return-1; $ad_data = $additional_data_returned; } # output XML data $xml_data = '' . $br; # this here to fix highlighting for text editor ' . $br; for ($i=0; $i<=$num; $i++) { if (! isset($scores_data[$i])) { break; } $record = explode('|', $scores_data[$i]); $xml_data .= $sp . '' . $br; $xml_data .= $sp . $sp . '' . htmlspecialchars($record[0]) . '' . $br; $xml_data .= $sp . $sp . '' . htmlspecialchars($record[1]) . '' . $br; for ($j=0; $j' . htmlspecialchars($record[$j+2]) . '' . $br; } $xml_data .= $sp . '' . $br; } $xml_data .= '
' . $br; return $xml_data; } function error($ouch) { global $return_type; # something went wrong if (preg_match('/plain/i', $return_type)) { $ouch = preg_replace('/\s/', '+', $ouch); print 'error='; } print $ouch; exit; } ?>