Removed the old unused stats code.

Fixes Debian bug #555276, CVE-2007-2383, CVE-2008-7720.
This commit is contained in:
Mark de Wever 2009-11-09 19:55:57 +00:00
parent 080cb58368
commit 48494a4b4b
170 changed files with 2 additions and 15049 deletions

View file

@ -32,6 +32,8 @@ Version 1.7.8-beta1:
(http://www.wesnoth.org/forum/viewtopic.php?f=4&t=27616)
* Removed some unused Drake macros from animation_utils
* Add recruitment anims for the Sky and Hurricane Drakes
* Removed the old stats code (Debian bug #555276, CVE-2007-2383,
CVE-2008-7720)
Version 1.7.7:
* AI:

View file

@ -1,19 +0,0 @@
CFLAGS=-Wall -g -Wunused-parameter -W -Wmissing-declarations
LDFLAGS=-lsqlite3 -lm
all: upload.cgi graph db2wml
upload.cgi: upload.cgi.o utils.o sqlite3_database.o
graph: graph.o utils.o sqlite3_database.o
db2wml: db2wml.o utils.o sqlite3_database.o
clean:
rm -f graph upload.cgi *.o
# Testsuite: .input* are fed into database, then .test run, compared with .result.
check: upload.cgi
@./run-tests.sh
clean-db: upload.cgi
rm -f wesnoth-uploads.db
./wesnoth-upload.cgi --initialize

View file

@ -1,14 +0,0 @@
# Shell routine to check QUERY_STRING args.
# Check vars are safe before we set them.
for f in `echo "$QUERY_STRING" | tr '&' ' '`; do
case "$f" in
W_*)
if echo "$f" | grep -qv '^[A-Za-z0-9_]*=[A-Za-z0-9_+.,-]*$'; then exit 1; fi
;;
*)
exit 1
;;
esac
done
eval `echo "$QUERY_STRING" | tr '&' ' '`

View file

@ -1,47 +0,0 @@
#! /bin/sh
GRAPH=/home/rusty/wesnoth/graph
DATABASE=/home/rusty/wesnoth/wesnoth-uploads.db
. check_args.sh
draw()
{
case "$W_TYPE" in
gold)
$GRAPH campaign_view gold 'start_turn = 0' $W_CAMPAIGN $W_DIFF $W_VERSION "" `echo $W_SCENARIOS | tr , ' '`;;
loss_abort)
$GRAPH campaign_view 'count(*)' 'result != 1' $W_CAMPAIGN $W_DIFF $W_VERSION "" `echo $W_SCENARIOS | tr , ' '`;;
wins)
$GRAPH campaign_view 'count(*)' 'result = 1' $W_CAMPAIGN $W_DIFF $W_VERSION "" `echo $W_SCENARIOS | tr , ' '`;;
win_turn)
$GRAPH campaign_view 'end_turn*100/(num_turns+1)' 'result = 1' $W_CAMPAIGN $W_DIFF $W_VERSION "" `echo $W_SCENARIOS | tr , ' '`;;
level)
$GRAPH units_view 'sum(count)' "start_turn = 0 AND level = $W_LEVEL" $W_CAMPAIGN $W_DIFF $W_VERSION "GROUP BY game" `echo $W_SCENARIOS | tr , ' '`;;
time)
$GRAPH campaign_view 'sum(time / 60)' 'time > 60' $W_CAMPAIGN $W_DIFF $W_VERSION "GROUP BY player" `echo $W_SCENARIOS | tr , ' '`;;
player)
$GRAPH --progress $W_CAMPAIGN $W_DIFF $W_VERSION $W_PLAYER `echo $W_SCENARIOS | tr , ' '`;;
esac
}
if [ -n "$W_PNG" ]; then
echo "Content-type: image/png"
echo
TMPFILE=`mktemp`
trap "rm -f $TMPFILE" 0
draw > $TMPFILE
# Hack: find viewBox.
X_Y=`sed -n 's/.*viewBox="-25 -25 \([0-9]*\) \([0-9]*\)".*/\1,\2/p' < $TMPFILE`
X=`echo $X_Y | cut -d, -f1`
Y=`echo $X_Y | cut -d, -f2`
if [ $Y -gt 160 ]; then
# Scale back to 160 high.
X=`echo "scale=2; $X * (160 / $Y)" | bc | cut -d. -f1`
Y=160
fi
rsvg -w$X -h$Y $TMPFILE /dev/fd/1
else
echo "Content-type: image/svg+xml"
echo
draw
fi

View file

@ -1,40 +0,0 @@
<div class="visualClear"></div>
</div>
</div> <!-- end content -->
<div id="footer">
<div class="visualClear"></div>
<div class="portlet" id="p-personal">
<ul>
<li id="pt-login">
<a href="http://www.wesnoth.org/mw/index.php?title=Special:Userlogin&amp;returnto=About">Create an account or log in</a>
</li>
</ul>
</div>
<div class="portlet" id="p-fixed">
<ul>
<li><a href="http://www.wesnoth.org/wiki/HomePage">Home</a></li>
<li><a href="http://www.wesnoth.org/wiki/Special:Recentchanges">Recent changes</a></li>
</ul>
</div>
<div class="visualClear"></div>
<div id="note">
<p>Copyright &copy; 2003-2005 The Battle for Wesnoth</p>
</div>
</div> <!-- end footer -->
</div> <!-- end main -->
</div> <!-- end global -->
</body>
</html>

View file

@ -1,45 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="KEYWORDS" content="Stats" />
<meta name="robots" content="index,follow" />
<link rel="shortcut icon" href="http://www.wesnoth.org/favicon.ico" />
<link rel="stylesheet" type="text/css" media="print" href="http://www.wesnoth.org/mw/skins/common/commonPrint.css" />
<link rel="shortcut icon" type="image/png" href="http://www.wesnoth.org/mw/skins/glamdrol/ico.png" />
<style type="text/css" media="screen,projection">/*<![CDATA[*/ @import "http://www.wesnoth.org/mw/skins/glamdrol/main.css"; /*]]>*/</style>
<script type="text/javascript" src="http://www.wesnoth.org/mw/index.php?title=-&amp;action=raw&amp;gen=js"></script><script type="text/javascript" src="http://www.wesnoth.org/mw/skins/common/wikibits.js"></script>
<title>Statistics - Gathered from "Help Wesnoth" Volunteers</title>
</head>
<body class="ns-0">
<div id="global">
<div id="header">
<div id="logo">
<a href="http://www.wesnoth.org/"><img alt="Wesnoth logo" src="http://www.wesnoth.org/mw/skins/glamdrol/wesnoth-logo.jpg" /></a>
</div>
</div>
<div id="nav">
<ul>
<li><a href="http://www.wesnoth.org/">Home</a></li>
<li><a href="http://www.wesnoth.org/wiki/Play">Play</a></li>
<li><a href="http://www.wesnoth.org/wiki/Create">Create</a></li>
<li><a href="http://www.wesnoth.org/forum/">Forums</a></li>
<li><a href="http://www.wesnoth.org/wiki/Support">Support</a></li>
<li><a href="http://www.wesnoth.org/wiki/Project">Project</a></li>
<li><a href="http://www.wesnoth.org/wiki/About">About</a></li>
</ul>
</div>
<div id="main">
<div id="content">
<a name="top" id="contentTop"></a>

View file

@ -1,413 +0,0 @@
#! /bin/sh
DATABASE=/home/rusty/wesnoth/wesnoth-uploads.db
URL="http://ozlabs.org/~rusty/stats.wesnoth.org/query-wesnoth.cgi"
print_header()
{
echo "<h1 class=\"firstHeading\">$1</h1>"
echo '<div id="bodyContent">'
echo '<h3 id="siteSub">From Wesnoth</h3>'
echo '<div id="contentSub"></div>'
}
get_scenario_list()
{
# Before 1.1.2, the versions weren't generally numbered. Now they
# should all be.
case $1 in
1.1-svn|1.1+svn|1.1.1|1.1.1+svn)
case $2 in
CAMPAIGN_HEIR_TO_THE_THRONE)
echo The_Elves_Besieged Blackwater_Port The_Isle_of_Anduin The_Bay_of_Pearls Muff_Malals_Peninsula Isle_of_the_Damned The_Siege_of_Elensefar Crossroads The_Princess_of_Wesnoth The_Valley_of_Death-The_Princesss_Revenge Gryphon_Mountain The_Ford_of_Abez Northern_Winter Mountain_Pass The_Dwarven_Doors Plunging_into_the_Darkness The_Lost_General Hasty_Alliance Scepter A_Choice_Must_Be_Made Snow_Plains Swamp_Of_Dread North_Elves Elven_Council valley_statue return_to_wesnoth trial_clans battle_for_wesnoth
;;
CAMPAIGN_TWO_BROTHERS)
# These are numbered!
/usr/bin/sqlite3 $DATABASE "select DISTINCT scenario FROM campaign_view WHERE campaign = '$2' AND version = '$1';" | sort -n
;;
*)
/usr/bin/sqlite3 $DATABASE "select DISTINCT scenario FROM campaign_view WHERE campaign = '$2' AND version = '$1';"
;;
esac
;;
*)
/usr/bin/sqlite3 $DATABASE "select DISTINCT scenario FROM campaign_view WHERE campaign = '$2' AND version = '$1';" | sort -n
;;
esac
}
# Returns sets of up to 10 comma-separated scenarios, given a scenario list.
chop_scenarios()
{
if [ $# = 1 ]; then
echo $1
return
fi
while [ $# -gt 8 ]; do
echo $1,$2,$3,$4,$5,$6,$7,$8
# Note that this deliberately repeats one.
shift 7
done
if [ $# -gt 1 ]; then
echo $@ | tr ' ' ','
fi
}
colorize()
{
sed -e 's,>aborted<,><font color=\"#ece000\">aborted</font><,g' -e 's,>victory<,><font color=\"green\">victory</font><,g' -e 's,>defeat<,><font color=\"#ff0d00\">defeat</font><,g'
}
# add_href field extra
add_href()
{
sed "s,^<TR><TD>\([^<]*\),<TR><TD><a href=\"?$1=\1\$2_VERSION\">\1,"
}
is_official()
{
case $1 in
1.1-svn)
case $2 in
CAMPAIGN_EASTERN_INVASION|CAMPAIGN_HEIR_TO_THE_THRONE|CAMPAIGN_SON_OF_THE_BLACK_EYE|CAMPAIGN_THE_RISE_OF_WESNOTH|CAMPAIGN_THE_DARK_HORDES|TUTORIAL)
return 0
;;
esac
return 1
;;
# Two brothers added.
1.1+svn|1.1.1)
case $2 in
CAMPAIGN_EASTERN_INVASION|CAMPAIGN_HEIR_TO_THE_THRONE|CAMPAIGN_SON_OF_THE_BLACK_EYE|CAMPAIGN_THE_RISE_OF_WESNOTH|CAMPAIGN_THE_DARK_HORDES|CAMPAIGN_TWO_BROTHERS|TUTORIAL)
return 0
;;
esac
return 1
;;
# 1.1.1+svn: Under the Burning Suns and South Guard added
# Son of Black Eye and Dark Hordes removed
*)
case $2 in
CAMPAIGN_DESERT|CAMPAIGN_THE_SOUTH_GUARD|CAMPAIGN_EASTERN_INVASION|CAMPAIGN_HEIR_TO_THE_THRONE|CAMPAIGN_THE_RISE_OF_WESNOTH|CAMPAIGN_TWO_BROTHERS|TUTORIAL)
return 0
;;
esac
return 1
;;
esac
}
print_main()
{
VERSIONS=`/usr/bin/sqlite3 $DATABASE "SELECT name FROM version_names;"`
# By default, use latest non-svn version.
if [ -z "$W_VERSION" ]; then
W_VERSION=`echo "$VERSIONS" | grep -v svn | tail -1`
fi
print_header "Wesnoth Statistics for $W_VERSION"
sep="Other versions available: "
for ver in $VERSIONS; do
if [ "$ver" != "$W_VERSION" ]; then
echo "$sep <a href=\"?W_VERSION=$ver\">$ver</a> "
sep="|"
fi
done
echo '<h2>Wesnoth Official Campaigns</h2>'
echo '<table border="1"><tr>'
echo '<th>Campaign Name</th>'
echo '<th>Difficulty</th>'
echo '<th>Games Uploaded</th>'
echo '</tr>'
/usr/bin/sqlite3 $DATABASE "SELECT campaign,difficulty,count(*) FROM campaign_view WHERE version='$W_VERSION' GROUP BY campaign,difficulty ORDER BY campaign,count(*) DESC;" | while IFS="|" read campaign diff count; do
if is_official $W_VERSION "$campaign"; then
echo "<TR><TD><a href=\"?W_CAMPAIGN=$campaign&W_DIFF=$diff&W_VERSION=$W_VERSION\">$campaign</a></TD><TD>$diff</TD><TD>$count</TD></TR>"
fi
done
echo '</table>'
echo '<h2>Wesnoth Unofficial Campaigns</h2>'
echo '<table border="1"><tr>'
echo '<th>Campaign Name</th>'
echo '<th>Difficulty</th>'
echo '<th>Games Uploaded</th>'
echo '</tr>'
/usr/bin/sqlite3 $DATABASE "SELECT campaign,difficulty,count(*) FROM campaign_view WHERE version='$W_VERSION' GROUP BY campaign,difficulty ORDER BY campaign,count(*) DESC;" | while IFS="|" read campaign diff count; do
if ! is_official $W_VERSION "$campaign"; then
echo "<TR><TD><a href=\"?W_CAMPAIGN=$campaign&W_DIFF=$diff&W_VERSION=$W_VERSION\">$campaign</a></TD><TD>$diff</TD><TD>$count</TD></TR>"
fi
done
echo '</table>'
echo "<h2><a href=\"?W_PLAYERS=1&W_VERSION=$W_VERSION\">List of Wesnoth Players</a></h2>"
}
print_players()
{
print_header "Wesnoth Players for $W_VERSION"
echo '<h2>Wesnoth Players</h2>'
echo '<table border="1"><tr>'
echo '<th>Player ID</th>'
echo '<th>Games uploaded</th>'
echo '<th>Last upload</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT DISTINCT campaign_view.player,game_count.games_received-1,game_count.last_upload FROM campaign_view,players,game_count WHERE campaign_view.version = '$W_VERSION' AND game_count.player_ref = players.rowid AND players.id = campaign_view.player;" | sed "s,^<TR><TD>\([^<]*\),<TR><TD><a href=\"?W_PLAYER=\1\&W_VERSION=$W_VERSION\">\1,"
echo '</table>'
}
add_graph()
{
echo "<object type=\"image/svg+xml\" data=\"draw_graph.cgi?$1\" NAME=\"$2\" width=\"100%\"><img src=\"draw_graph.cgi?$1&W_PNG=1\"></object>"
}
# Graph out all campaigns a player played (unless specific requested, from graph)
do_graph_player()
{
echo '(<font color="green">Green</font> means victory, <font color="#ff0d00">red</font> means defeat, <font color="#ece000">yellow</font> means quit. Bars indicate start/finish turn).<br>'
if [ -n "$W_CAMPAIGN" ]; then
EXTRA="AND campaign='$W_CAMPAIGN'"
fi
for campaign_diff in `/usr/bin/sqlite3 $DATABASE "SELECT DISTINCT campaign,difficulty FROM campaign_view WHERE player='$W_PLAYER' AND version='$W_VERSION' $EXTRA;"`; do
campaign=`echo "$campaign_diff" | cut -d\| -f1`
diff=`echo "$campaign_diff" | cut -d\| -f2`
SCENARIOS=$(chop_scenarios $(/usr/bin/sqlite3 $DATABASE "SELECT DISTINCT scenario FROM campaign_view WHERE player='$W_PLAYER' AND version='$W_VERSION' AND difficulty='$diff' AND campaign='$campaign';") )
echo "Campaign $campaign ($diff):"
for scen in $SCENARIOS; do
add_graph "W_SCENARIOS=$scen&W_VERSION=$W_VERSION&W_CAMPAIGN=$campaign&W_DIFF=$diff&W_PLAYER=$W_PLAYER&W_TYPE=player" "Player progress"
done
done
echo "<h2><a href=\"?W_PLAYERP=$W_PLAYER&W_VERSION=$W_VERSION\">Statistics in detail</a></h2>"
}
graph_player()
{
print_header "Wesnoth Player $W_PLAYER"
do_graph_player
}
# Print out all scenarios a player played.
print_player()
{
print_header "Wesnoth Player $W_PLAYER"
echo '<table border="1"><tr>'
echo '<th>ID</th>'
echo '<th>Campaign Name</th>'
echo '<th>Scenario</th>'
echo '<th>Difficulty</th>'
echo '</tr>'
i=1
/usr/bin/sqlite3 $DATABASE "SELECT DISTINCT campaign,scenario,difficulty FROM campaign_view WHERE player='$W_PLAYERP' AND version='$W_VERSION';" | while IFS='|' read campaign scenario diff; do
echo "<TR><TD><a href=\"?W_PLAYERSCENARIO=$i&W_PLAYER=$W_PLAYERP&W_CAMPAIGN=$campaign&W_SCENARIO=$scenario&W_DIFF=$diff&W_VERSION=$W_VERSION\">$i</TD><TD>$campaign</TD><TD>$scenario</TD><TD>$diff</TD></TR>"
i=$(($i + 1))
done
echo '</table>'
}
# "Type" "Name" scenarios...
add_scenario_graphs()
{
ASG_TYPE=$1
ASG_NAME=$2
shift 2
for scen; do
add_graph "W_SCENARIOS=$scen&W_VERSION=$W_VERSION&W_DIFF=$W_DIFF&W_CAMPAIGN=$W_CAMPAIGN&W_TYPE=$ASG_TYPE" "$ASG_NAME"
done
}
# Graph the campaign on a given campaign_names.rowid
graph_campaign()
{
print_header "Wesnoth $W_CAMPAIGN ($W_DIFF)"
SCENARIOS=$(chop_scenarios $(get_scenario_list $W_VERSION $W_CAMPAIGN) )
echo "<h2>Gold at start of game</h2>"
add_scenario_graphs "gold" "Gold" $SCENARIOS
echo "<h2>Losses/quits</h2>"
add_scenario_graphs "loss_abort" "Losses and quits" $SCENARIOS
echo "<h2>Wins</h2>"
add_scenario_graphs "wins" "Victories" $SCENARIOS
echo "<h2>Percent turns used on victory</h2>"
add_scenario_graphs "win_turn" "Percent turns used" $SCENARIOS
echo "<h2>Total minutes per player</h2>"
add_scenario_graphs "time" "Minutes spent" $SCENARIOS
for level in 1 2 3; do
echo "<h2>Count of Level $level units at start of game</h2>"
add_scenario_graphs "level&W_LEVEL=$level" "Level $level units" $SCENARIOS
done
echo "<h2><a href=\"?W_CAMPAIGNP=$W_CAMPAIGN&W_DIFF=$W_DIFF&W_VERSION=$W_VERSION\">Statistics in detail</a></h2>"
}
# Print out details on a given campaign
print_campaign()
{
print_header "Wesnoth $W_CAMPAIGN ($W_DIFF)"
echo '<table border="1"><tr>'
echo '<th>Scenario</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT DISTINCT scenario FROM campaign_view WHERE campaign = '$W_CAMPAIGNP' AND difficulty = '$W_DIFF' AND version = '$W_VERSION';"| sed "s,^<TR><TD>\([^<]*\),<TR><TD><a href=\"?W_SCENARIO=\1\&W_CAMPAIGN=$W_CAMPAIGNP\&W_VERSION=$W_VERSION\&W_DIFF=$W_DIFF\">\1,"
echo '</table>'
}
# Print out details on a given scenario
print_scenario()
{
print_header "Wesnoth Scenario $W_SCENARIO ($W_CAMPAIGN $W_DIFF)"
echo '<table border="1"><tr>'
echo '<th>Game ID</th>'
echo '<th>Player</th>'
echo '<th>Start turn</th>'
echo '<th>Start gold</th>'
echo '<th>Time taken (sec)</th>'
echo '<th>Result</th>'
echo '<th>End turn</th>'
echo '<th>Num turns</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT game,player,start_turn,gold,time,CASE result WHEN 0 THEN 'aborted' WHEN 1 THEN 'victory' ELSE 'defeat' END, end_turn, num_turns FROM campaign_view WHERE scenario = '$W_SCENARIO' AND campaign = '$W_CAMPAIGN' AND difficulty = '$W_DIFF';" | sed "s,^<TR><TD>\([^<]*\),<TR><TD><a href=\"?W_GAME=\1\">\1," | colorize
echo '</table>'
}
# Print out details on a given scenario.
print_player_scenario()
{
print_header "Wesnoth Player $W_PLAYER playing $W_SCENARIO ($W_CAMPAIGN $W_DIFF)"
echo "Number of turns in scenario: "
# Ideally, only returns one number...
/usr/bin/sqlite3 $DATABASE "SELECT DISTINCT num_turns from campaign_view WHERE scenario='$W_SCENARIO' AND campaign='$W_CAMPAIGN' AND player='$W_PLAYER' AND difficulty='$W_DIFF' AND version='$W_VERSION';"
echo '<table border="1"><tr>'
echo '<th>ID</th>'
echo '<th>Start turn</th>'
echo '<th>Start gold</th>'
echo '<th>Time taken (sec)</th>'
echo '<th>End turn</th>'
echo '<th>Result</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT game,start_turn,gold,time,end_turn,CASE result WHEN 0 THEN 'aborted' WHEN 1 THEN 'victory' ELSE 'defeat' END FROM campaign_view WHERE version='$W_VERSION' AND player='$W_PLAYER' AND scenario='$W_SCENARIO' AND difficulty='$W_DIFF' AND campaign='$W_CAMPAIGN' ORDER BY game;" | sed "s,^<TR><TD>\([^<]*\),<TR><TD><a href=\"?W_GAME=\1\">\1," | colorize
echo '</table>'
}
# Print out details and units for a given game.
print_game()
{
print_header "Details of game $W_GAME"
echo '<table border="1"><tr>'
/usr/bin/sqlite3 $DATABASE "SELECT campaign,difficulty,scenario,player,version,gold,start_turn,end_turn,num_turns,time,result FROM campaign_view WHERE game='$W_GAME';" | (IFS="|" read cam diff scen player ver gold st et nt time result
echo "Campaign: $cam<br>"
echo "Difficulty: $diff<br>"
echo "Scenario: $scen<br>"
echo "Player: $player<br>"
echo "Version: $ver<br>"
echo "Start gold: $gold<br>"
echo "Start turn: $st<br>"
echo "End turn: $et<br>"
echo "Num turns: $nt<br>"
echo "Time taken (sec): $time<br>"
echo "Result:"
case $result in
0) echo "<font color=\"#ece000\">aborted</font>";;
1) echo "<font color=\"green\">victory</font>";;
2) echo "<font color=\"#ff0d00\">defeat</font>";;
esac
echo "<br>")
echo "<h2>Important Unit Stats (at Start of Game)</h2>"
echo '<table border="1"><tr>'
echo '<th>Name</th>'
echo '<th>Level</th>'
echo '<th>Experience</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT unit_names.name,special_units.level,special_units.experience FROM special_units,unit_names WHERE special_units.game_ref=$W_GAME AND unit_names.rowid=special_units.unit_name_ref;"
echo '</table>'
echo "<h2>Wesnoth Unit Summary (at Start of Game)</h2>"
echo '<table border="1"><tr>'
echo '<th>Type</th>'
echo '<th>Level</th>'
echo '<th>Number</th>'
echo '</tr>'
/usr/bin/sqlite3 -html $DATABASE "SELECT unit_types.name,unit_types.level,unit_tallies.count FROM unit_types,unit_tallies WHERE unit_tallies.game_ref=$W_GAME AND unit_types.rowid=unit_tallies.unit_type_ref ORDER BY unit_types.level DESC,unit_types.name;"
echo '</table>'
}
# Simple access page which is given in the Help Wesnoth dialog
my_page()
{
VERSIONS=`/usr/bin/sqlite3 $DATABASE "SELECT DISTINCT version FROM campaign_view WHERE player=$W_P;"`
# By default, use latest version for this player
if [ -z "$W_VERSION" ]; then
W_VERSION=`echo "$VERSIONS" | tail -1`
fi
print_header "Wesnoth Page for Player $W_PLAYER"
echo "Versions played: "
sep=""
for ver in $VERSIONS; do
if [ "$ver" = "$W_VERSION" ]; then
echo "$sep <u>$W_VERSION</u>"
else
echo "$sep <a href=\"?W_P=$W_P&W_VERSION=$ver\">$ver</a> "
fi
sep="|"
done
W_PLAYER=$W_P do_graph_player
}
echo "Content-type: text/html"
echo
cat /home/rusty/public_html/stats.wesnoth.org/header.html
# We accept a simple numeric arg for player id.
if echo "$QUERY_STRING" | grep -q '^[0-9][0-9]*$'; then
QUERY_STRING="W_P=$QUERY_STRING"
fi
. check_args.sh
case "$QUERY_STRING" in
W_PLAYER=*)
graph_player
;;
W_PLAYERP=*)
print_player
;;
W_CAMPAIGN=*)
graph_campaign
;;
W_CAMPAIGNP=*)
print_campaign
;;
W_SCENARIO=*)
print_scenario
;;
W_PLAYERSCENARIO=*)
print_player_scenario
;;
W_GAME=*)
print_game
;;
W_P=*)
my_page
;;
W_PLAYERS=*)
print_players
;;
*)
print_main
;;
esac
echo '<div id="lastmod"> This page based generated from a database, which was last modified '`date -u -r $DATABASE`'.</div>'
cat /home/rusty/public_html/stats.wesnoth.org/footer.html

View file

@ -1,30 +0,0 @@
/* Simple SQL-style database ops. Currently implemented for sqlite3. */
#ifndef _UPLOAD_ANALYSIS_DATABASE_H
#define _UPLOAD_ANALYSIS_DATABASE_H
#include <stdbool.h>
/* Returns handle to the database.. */
void *db_open(const char *file);
/* Runs query (SELECT). Fills in columns. */
struct db_query
{
unsigned int num_rows;
char ***rows;
};
struct db_query *db_query(void *h, const char *query);
/* Runs command (CREATE TABLE/INSERT) */
void db_command(void *h, const char *command);
/* Starts transaction. Doesn't need to nest. */
void db_transaction_start(void *h);
/* Finishes transaction, or rolls it back and caller needs to start again. */
bool db_transaction_finish(void *h);
/* Closes database (only called when everything OK). */
void db_close(void *h);
#endif /* _UPLOAD_ANALYSIS_DATABASE_H */

View file

@ -1,121 +0,0 @@
/* Create WML upload file for a given game. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "database.h"
#include "utils.h"
#define DATABASE_FILE "/home/rusty/wesnoth/wesnoth-uploads.db"
int log_fd = STDERR_FILENO;
int main(int argc, char *argv[])
{
void *h;
char *q, *filename;
unsigned int i;
int level;
struct db_query *query;
if (argc != 3)
barf("Usage: %s <database> <games.rowid>\n", argv[0]);
h = db_open(argv[1]);
/*
campaign="CAMPAIGN_HEIR_TO_THE_THRONE"
difficulty="NORMAL"
scenario="Mountain_Pass"
gold="549"
time="2052"
num_turns="25"
start_turn="10"
version="1.1-svn"
*/
q = aprintf("SELECT campaign_names.name,difficulty_names.name,scenario_names.name,games.gold,games.start_time,games.start_turn,version_names.name,games.result,games.end_time,games.end_gold,games.game_number,players.id FROM players,campaign_names,difficulty_names,scenario_names,campaigns,scenarios,games,version_names WHERE games.rowid='%s' AND games.scenario_ref = scenarios.rowid AND scenarios.campaign_ref = campaigns.rowid AND campaigns.difficulty_name_ref = difficulty_names.rowid AND campaigns.campaign_name_ref = campaign_names.rowid AND games.version_name_ref = version_names.rowid AND campaigns.player_ref = players.rowid AND scenario_names.rowid = scenarios.scenario_name_ref;", argv[2]);
query = db_query(h, q);
if (atoi(query->rows[0][8]) - atoi(query->rows[0][4]) < 100) {
fprintf(stderr, "Only lasted from '%s' to '%s'\n",
query->rows[0][4], query->rows[0][8]);
exit(0);
}
filename = aprintf("%08i", atoi(query->rows[0][10]));
dup2(open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666), STDOUT_FILENO);
printf("format_version=\"1\"\n"
"version=\"%s\"\n"
"id=\"%s\"\n",
query->rows[0][6],
query->rows[0][11]);
printf("[game]\n"
"\tcampaign=\"%s\"\n"
"\tdifficulty=\"%s\"\n"
"\tscenario=\"%s\"\n"
"\tgold=\"%s\"\n"
"\ttime=\"%s\"\n"
"\tstart_turn=\"%s\"\n",
query->rows[0][0],
query->rows[0][1],
query->rows[0][2],
query->rows[0][3],
query->rows[0][4],
query->rows[0][5]);
if (streq(query->rows[0][7], "0")) {
printf("\t[quit]\n"
"\t\ttime=\"%s\"\n"
"\t[/quit]\n",
query->rows[0][8]);
} else if (streq(query->rows[0][7], "1")) {
printf("\t[victory]\n"
"\t\ttime=\"%s\"\n"
"\t\tgold=\"%s\"\n"
"\t[/victory]\n",
query->rows[0][8],
query->rows[0][9]);
} else {
printf("\t[defeat]\n"
"\t\ttime=\"%s\"\n"
"\t[/defeat]\n",
query->rows[0][8]);
}
q = aprintf("SELECT unit_names.name,special_units.level,special_units.experience FROM unit_names,special_units WHERE special_units.game_ref = '%s' AND unit_names.rowid = special_units.unit_name_ref;", argv[2]);
query = db_query(h, q);
for (i = 0; i < query->num_rows; i++)
printf("\t[special-unit]\n"
"\t\tname=\"%s\"\n"
"\t\tlevel=\"%s\"\n"
"\t\texperience=\"%s\"\n"
"\t[/special-unit]\n",
query->rows[i][0], query->rows[i][1],query->rows[i][2]);
level = -1;
printf("\t[units-by-level]\n");
q = aprintf("SELECT unit_types.level,unit_types.name,unit_tallies.count FROM unit_tallies,unit_types WHERE unit_tallies.game_ref = '%s' AND unit_types.rowid = unit_tallies.unit_type_ref ORDER BY unit_types.level ASC;", argv[2]);
query = db_query(h, q);
for (i = 0; i < query->num_rows; i++) {
if (atoi(query->rows[i][0]) != level) {
if (i != 0)
printf("\t\t[/%i]\n", level);
level = atoi(query->rows[i][0]);
printf("\t\t[%i]\n", level);
}
printf("\t\t\t[%s]\n"
"\t\t\t\tcount=\"%s\"\n"
"\t\t\t[/%s]\n",
query->rows[i][1], query->rows[i][2],query->rows[i][1]);
}
if (i != 0)
printf("\t\t[/%i]\n", level);
printf("\t[/units-by-level]\n"
"[/game]\n");
return 0;
}

View file

@ -1,287 +0,0 @@
/* Query database to produce a graph of a campaign as SVG. */
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "utils.h"
#include "database.h"
#define DATABASE_FILE "/home/rusty/wesnoth/wesnoth-uploads.db"
#define HEIGHT 200
#define WIDTH 130 /* For each entry */
#define PROGRESS_WIDTH 150
#define BORDER 25
#define BAR_HEIGHT 10
int log_fd = STDERR_FILENO;
static void print_circle(unsigned int x, unsigned int val,
unsigned int repeats,
const char *player,
const char *campaign, const char *ver)
{
/* Some queries (eg. # wins) not associated with particular player. */
if (player)
printf("<a xlink:href=\"index.cgi"
"?W_PLAYER=%s&amp;W_CAMPAIGN=%s&amp;W_VERSION=%s\""
" xlink:title=\"Player %s\" target=\"_blank\">",
player, campaign, ver, player);
printf("<circle cx=\"%u\" cy=\"%u\" r=\"%f\" fill=\"red\"/>",
x, HEIGHT - val, sqrt(repeats)*2);
if (player)
printf("</a>");
printf("\n");
}
static void plot(struct db_query *query, unsigned int x, float scale,
const char *campaign, const char *ver)
{
unsigned int i, prev = 0;
for (i = 0; i < query->num_rows; i++) {
if (streq(query->rows[i][0], query->rows[prev][0]))
continue;
print_circle(x, atoi(query->rows[prev][0])*scale, i-prev,
query->rows[prev][1], campaign, ver);
prev = i;
}
if (i != 0)
print_circle(x, atoi(query->rows[prev][0])*scale, i-prev,
query->rows[prev][1], campaign, ver);
}
static void draw_graph(const char *view, const char *answer,
const char *select_cond, const char *campaign,
const char *diff, const char *ver,
const char *extra,
int argc, char *argv[])
{
void *h;
char *querystr;
struct db_query *query[argc];
int i, minimum = 0, maximum = 0;
unsigned int j, span, grad;
float scale;
h = db_open(DATABASE_FILE);
for (i = 0; i < argc; i++) {
querystr = aprintf("SELECT %s,player FROM %s WHERE %s AND version='%s' AND difficulty='%s' AND scenario='%s' AND campaign='%s' %s;",
answer, view, select_cond, ver, diff,
argv[i], campaign, extra);
query[i] = db_query(h, querystr);
for (j = 0; j < query[i]->num_rows; j++) {
maximum = max(atoi(query[i]->rows[j][0]), maximum);
minimum = min(atoi(query[i]->rows[j][0]), minimum);
}
#if 0
printf("%s: ", argv[i]);
for (j = 0; j < query[i]->num_rows; j++)
printf("%s ", query[i]->rows[j][0]);
printf("\n");
#endif
}
/* Header and axes. */
printf("<?xml version=\"1.0\" standalone=\"no\"?>\n"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\" \"svg-20001102.dtd\">"
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"-%u -%u %u %u\" >"
"<path fill=\"none\" stroke=\"black\" d=\"M 0 %u l 0 -%u m -15 25 l 15 -25 l 15 25\"/>"
"<path fill=\"none\" stroke=\"black\" d=\"M 0 %u l %i 0 m -25 -15 l 25 15 l -25 15\"/>\n",
BORDER, BORDER, argc*WIDTH+BORDER*2, HEIGHT+BORDER*2,
HEIGHT, HEIGHT, HEIGHT, argc*WIDTH);
/* Print names of scenarios and marks along x axis. */
for (i = 0; i < argc; i++) {
char *name = strndup(argv[i], 15);
while (strchr(name, '_'))
*strchr(name, '_') = ' ';
printf("<text text-anchor=\"middle\" text-align=\"center\" font-size=\"16\" fill=\"black\" x=\"%u\" y=\"%u\">%s</text>\n"
"<line stroke=\"black\" x1=\"%u\" y1=\"%u\" x2=\"%u\" y2=\"%u\"/>\n",
i*WIDTH+WIDTH/2, HEIGHT+20, name,
i*WIDTH+WIDTH/2, HEIGHT, i*WIDTH+WIDTH/2, HEIGHT+5);
}
span = maximum - minimum;
/* We want a Y size of HEIGHT. */
scale = (float)HEIGHT / span;
if (span > 200)
grad = 100;
else if (span > 20)
grad = 10;
else
grad = 1;
/* Print gradiations up y axis. */
for (i = (minimum+grad-1)/grad*grad; i < maximum; i += grad)
printf("<text fill=\"black\" x=\"-25\" y=\"%f\">%u</text>\n"
"<line stroke=\"black\" x1=\"-5\" y1=\"%f\" x2=\"0\" y2=\"%f\"/>\n",
HEIGHT - scale*i, i, (span-i)*scale, (span-i)*scale);
/* Now plot results. We compine multiples into bigger circles. */
for (i = 0; i < argc; i++)
plot(query[i], i*WIDTH+WIDTH/2, scale, campaign, ver);
/* Draw average */
printf("<path fill=\"none\" stroke=\"gray\" d=\"");
for (i = 0; i < argc; i++) {
unsigned int sum = 0;
float avg;
for (j = 0; j < query[i]->num_rows; j++)
sum += atoi(query[i]->rows[j][0]);
if (query[i]->num_rows == 0)
avg = 0;
else
avg = (float)sum / query[i]->num_rows;
printf("%s %u %f ", i == 0 ? "M" : "L",
i*WIDTH+WIDTH/2, HEIGHT - avg*scale);
}
printf("\"/>\n");
/* Print 0 line if not at bottom of graph. */
if (minimum != 0)
printf("<line stroke=\"red\" x1=\"%u\" y1=\"%f\" x2=\"%u\" y2=\"%f\"/>\n",
0, maximum*scale, argc*WIDTH, maximum*scale);
printf("</svg>\n");
}
static void draw_one(struct db_query *query, unsigned height,
unsigned int argc, char **argv)
{
unsigned int i, j;
/* Axes */
printf("<path fill=\"none\" stroke=\"black\" d=\"M 0 %u l 0 -%u m -15 25 l 15 -25 l 15 25\"/>"
"<path fill=\"none\" stroke=\"black\" d=\"M 0 %u l %i 0 m -25 -15 l 25 15 l -25 15\"/>\n",
height, height, height, argc*PROGRESS_WIDTH);
/* Print names of scenarios along x axis, with lines. */
for (i = 0; i < argc; i++) {
char *name = strndup(argv[i], 15);
while (strchr(name, '_'))
*strchr(name, '_') = ' ';
printf("<text text-anchor=\"middle\" text-align=\"center\" font-size=\"16\" fill=\"black\" x=\"%u\" y=\"%u\">%s</text>\n"
"<line stroke=\"black\" x1=\"%u\" y1=\"%u\" x2=\"%u\" y2=\"%u\"/>\n",
i*PROGRESS_WIDTH+PROGRESS_WIDTH/2, height+20, name,
i*PROGRESS_WIDTH+PROGRESS_WIDTH, 0,
i*PROGRESS_WIDTH+PROGRESS_WIDTH, height+5);
}
/* Draw each game. */
for (i = 0; i < query->num_rows; i++) {
int start, end, num_turns;
/* Find which scenario was played. */
for (j = 0; j < argc; j++) {
if (streq(query->rows[i][0], argv[j]))
break;
}
if (j == argc)
continue;
/* Bar goes from start to end turn. */
start = atoi(query->rows[i][1]);
end = atoi(query->rows[i][2]);
/* Old data has no end 8( */
if (end < start)
end = start;
/* We can end one turn *after* last turn (timeout). */
num_turns = atoi(query->rows[i][3])+1;
start /= (float)num_turns/PROGRESS_WIDTH;
end /= (float)num_turns/PROGRESS_WIDTH;
/* Link this to the specific game stats. */
printf("<a xlink:href=\"index.cgi?W_GAME=%s\""
" xlink:title=\"Game %s\" target=\"_blank\">",
query->rows[i][5], query->rows[i][5]);
printf("<rect x=\"%u\" y=\"%u\" width=\"%u\" height=\"%u\""
" stroke=\"black\" fill=\"%s\"/></a>\n",
PROGRESS_WIDTH*j+start, height-BAR_HEIGHT,
end-start?:1, BAR_HEIGHT,
streq(query->rows[i][4], "1") ? "green"
: streq(query->rows[i][4], "2") ? "#ff0d00"
: "#ece000");
height -= BAR_HEIGHT;
}
}
/* Draw the progress of this player. */
static void draw_progress_graph(const char *campaign, const char *diff,
const char *ver, const char *player,
unsigned int argc, char *argv[])
{
void *h;
char *querystr;
struct db_query *query;
unsigned int height, i, j;
h = db_open(DATABASE_FILE);
querystr = aprintf("SELECT scenario,start_turn,end_turn,num_turns,result,game FROM campaign_view WHERE campaign='%s' AND difficulty='%s' AND version='%s' AND player='%s' ORDER BY game;",
campaign, diff, ver, player);
query = db_query(h, querystr);
/* Remove scenarios not played by player. */
for (i = 0; i < argc; i++) {
for (j = 0; j < query->num_rows; j++) {
if (streq(query->rows[j][0], argv[i]))
break;
}
if (j == query->num_rows) {
delete_arr(argv, argc, i, 1);
argc--;
i--;
}
}
height = 0;
/* For each one we're going to draw, add BAR_HEIGHT to height */
for (i = 0; i < argc; i++) {
for (j = 0; j < query->num_rows; j++) {
if (streq(query->rows[j][0], argv[i]))
height += BAR_HEIGHT;
}
}
/* Header. */
printf("<?xml version=\"1.0\" standalone=\"no\"?>\n"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\" \"svg-20001102.dtd\">"
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"-%u -%u %u %u\" preserveAspectRatio=\"none\">\n",
BORDER, BORDER, argc*PROGRESS_WIDTH+BORDER*2, height+BORDER*2);
draw_one(query, height, argc, argv);
printf("</svg>\n");
}
static void usage(const char *name)
{
barf("Usage: %s <view> <answer> <select-cond> <campaign> <difficulty> <version> <extra-args> <scenario>...\n"
" OR:\n"
" %s --progress <campaign> <difficulty> <version> <scenario_name>...",
name, name);
}
/* FIXME: Allow multiple queries plotted with different colors? */
int main(int argc, char *argv[])
{
if (argv[1] && streq(argv[1], "--progress")) {
if (argc < 7)
usage(argv[0]);
draw_progress_graph(argv[2], argv[3], argv[4], argv[5],
argc-6, argv+6);
} else {
if (argc < 9)
usage(argv[0]);
draw_graph(argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argc - 8, argv + 8);
}
return 0;
}

View file

@ -1,62 +0,0 @@
#! /bin/sh
set -e
TESTING_DATABASE=/tmp/wesnoth-uploads.db
TESTING_LOGFILE=/tmp/wesnoth-upload-log.
export TESTING_DATABASE
export TESTING_LOGFILE
for f in testsuite/*.input; do
echo $f
rm -f $TESTING_DATABASE $TESTING_LOGFILE*
./upload.cgi --initialize
echo 'INSERT INTO bad_serial VALUES ("BADSERIAL");' | sqlite3 $TESTING_DATABASE
for i in $f*; do
case $i in *~) :;; *)
if ./upload.cgi < $i > /dev/null; then :
else echo ERROR:; cat ${TESTING_LOGFILE}0; exit 1
fi;;
esac
done
BASE=`echo $f | sed 's/\.input$//'`
[ x"`sqlite3 $TESTING_DATABASE < $BASE.test`" = x"`cat $BASE.output`" ]
done
echo -n Parallel test
rm -f $TESTING_DATABASE $TESTING_LOGFILE*
./upload.cgi --initialize
parallel_test()
{
while [ -f $1 ]; do sleep 0; done
if ./upload.cgi < $2 > /dev/null; then
echo $2 succeeded >> $3
echo -n .
else
echo $2 failed >> $3
fi
}
STARTFILE=`mktemp`
OUTPUTFILE=`mktemp`
trap "rm -f $OUTPUTFILE $STARTFILE" EXIT
COUNT=0
for i in testsuite/*.parallel; do
COUNT=$(($COUNT + 1))
parallel_test $STARTFILE $i $OUTPUTFILE &
done
rm $STARTFILE
while [ `wc -l < $OUTPUTFILE` -lt $COUNT ]; do
sleep 1
done
echo
if grep failed $OUTPUTFILE; then
exit 1
fi
[ x"`sqlite3 $TESTING_DATABASE < testsuite/parallel-test`" = x"`cat testsuite/parallel-output`" ]
echo Succeeded.

View file

@ -1,102 +0,0 @@
/* SQLite3 database backend. */
#include <stdlib.h>
#include <unistd.h>
#include <sqlite3.h>
#include "database.h"
#include "utils.h"
/* sqlite3_busy_timeout sleeps for a *second*. What a piece of shit. */
static int busy(void *unused __attribute__((unused)), int count)
{
usleep(50000);
/* If we've been stuck for 1000 iterations (at least 50
* seconds), give up. */
return (count < 1000);
}
void *db_open(const char *file)
{
sqlite3 *handle;
int err = sqlite3_open(file, &handle);
if (err != SQLITE_OK)
barf("Error %i from sqlite3_open of db '%s'\n", err, file);
sqlite3_busy_handler(handle, busy, NULL);
return handle;
}
static int query_cb(void *data, int num, char**vals,
char**names __attribute__((unused)))
{
int i;
struct db_query *query = data;
query->rows = realloc_array(query->rows, query->num_rows+1);
query->rows[query->num_rows] = new_array(char *, num);
for (i = 0; i < num; i++) {
/* We don't count rows with NULL results
* (eg. count(*),player where count turns out to be
* zero. */
if (!vals[i])
return 0;
query->rows[query->num_rows][i] = strdup(vals[i]);
}
query->num_rows++;
return 0;
}
/* Runs query (SELECT). Fails if > 1 row returned. Fills in columns. */
struct db_query *db_query(void *h, const char *query)
{
struct db_query *ret = new(struct db_query);
char *err;
ret->rows = NULL;
ret->num_rows = 0;
if (sqlite3_exec(h, query, query_cb, ret, &err) != SQLITE_OK)
barf("Failed sqlite3 query '%s': %s", query, err);
return ret;
}
/* Runs command (CREATE TABLE/INSERT) */
void db_command(void *h, const char *command)
{
char *err;
if (sqlite3_exec(h, command, NULL, NULL, &err) != SQLITE_OK)
barf("Failed sqlite3 command '%s': %s", command, err);
}
/* Starts transaction. Doesn't need to nest. */
void db_transaction_start(void *h)
{
char *err;
if (sqlite3_exec(h, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, &err)!=SQLITE_OK)
barf("Starting sqlite3 transaction: %s\n", err);
}
/* Finishes transaction, or rolls it back and caller needs to start again. */
bool db_transaction_finish(void *h)
{
switch (sqlite3_exec(h, "COMMIT TRANSACTION;", NULL, NULL, NULL)) {
case SQLITE_OK:
return true;
case SQLITE_BUSY:
if (sqlite3_exec(h, "ROLLBACK TRANSACTION;", NULL, NULL, NULL)
!= SQLITE_OK)
barf("Ending sqlite3 busy rollback failed");
return false;
default:
barf("Strange sqlite3 error return from COMMIT");
}
}
/* Closes database (only called when everything OK). */
void db_close(void *h)
{
sqlite3_close(h);
}

View file

@ -1,28 +0,0 @@
format_version="1"
id="587613382338525"
serial="BADSERIAL"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,3 +0,0 @@
/* Returns NEXT game number. */
SELECT games_received FROM game_count;

View file

@ -1,28 +0,0 @@
format_version="1"
id="587613382338525"
serial="115726044800000149"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="587613382338525"
serial="115726044800000149"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,3 +0,0 @@
/* Returns NEXT game number. */
SELECT games_received FROM game_count;

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385251"
serial="1157260448000001491"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,27 +0,0 @@
format_version="1"
id="587613382338525"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,27 +0,0 @@
format_version="1"
id="587613382338525"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1 +0,0 @@
3

View file

@ -1,3 +0,0 @@
/* Returns NEXT game number. */
SELECT games_received FROM game_count;

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385252"
serial="1157260448000001492"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385253"
serial="1157260448000001493"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385254"
serial="1157260448000001494"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385255"
serial="1157260448000001495"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385256"
serial="1157260448000001496"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385257"
serial="1157260448000001497"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385258"
serial="1157260448000001498"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,28 +0,0 @@
format_version="1"
id="5876133823385259"
serial="1157260448000001499"
version="1.1.9+svn"
[game]
campaign="CAMPAIGN_TEST"
difficulty="NORMAL"
gold="1000"
num_turns="100"
scenario="Test"
time="41"
[special-unit]
experience="0"
level="1"
name="Side1"
[/special-unit]
[units-by-level]
[1]
[Elvish Scout]
count="1"
[/Elvish Scout]
[/1]
[/units-by-level]
[quit]
end_turn="12"
time="1598"
[/quit]
[/game]

View file

@ -1,18 +0,0 @@
1157260448000001491
1157260448000001492
1157260448000001493
1157260448000001494
1157260448000001495
1157260448000001496
1157260448000001497
1157260448000001498
1157260448000001499
5876133823385251
5876133823385252
5876133823385253
5876133823385254
5876133823385255
5876133823385256
5876133823385257
5876133823385258
5876133823385259

View file

@ -1,3 +0,0 @@
/* Check all the serial numbers are in there */
select * from serial order by id;
select * from players order by id;

View file

@ -1,823 +0,0 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#include "database.h"
#include "utils.h"
#define MAX_LOGS 10
int log_fd = STDERR_FILENO;
static const char *database_file(void)
{
return getenv("TESTING_DATABASE") ?:
"/home/rusty/wesnoth/wesnoth-uploads.db";
}
static const char *logfile_prefix(void)
{
return getenv("TESTING_LOGFILE") ?:
"/home/rusty/wesnoth/wesnoth-upload-log.";
}
/* We open a log file, and delete if if all goes well. We
* keep up a max of MAX_LOGS files, to avoid filling disk. */
static void maybe_log_to_file(char **filename)
{
char name[strlen(logfile_prefix()) + sizeof(__stringify(MAX_LOGS))];
unsigned int i;
for (i = 0; i < MAX_LOGS; i++) {
struct stat st;
sprintf(name, "%s%u", logfile_prefix(), i);
if (lstat(name, &st) != 0) {
log_fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0640);
if (log_fd >= 0) {
*filename = strdup(name);
return;
}
}
}
*filename = NULL;
log_fd = open("/dev/null", O_WRONLY);
if (log_fd < 0)
barf_perror("Could not open /dev/null for logging");
}
struct wml
{
const char *name;
unsigned int num_keys;
const char **keyval;
unsigned int num_children;
struct wml **child;
};
static struct wml *new_wml(const char *name)
{
struct wml *wml = new(struct wml);
wml->name = name;
wml->num_keys = wml->num_children = 0;
wml->keyval = NULL;
wml->child = NULL;
return wml;
}
/* Deliberately simple parser (doesn't handle comments, for example).
* We want canonical forms so we can enter into database.
*/
static char *get_line(char **string)
{
char *start, *first_quote = NULL;
/* Ignore whitespace. */
while (isspace(**string))
(*string)++;
if (**string == '\0')
return NULL;
start = *string;
while (**string != '\n') {
switch (**string) {
case '\0':
barf("Unexpected end of input");
case '+':
if (!first_quote)
barf("Cannot handle '+'");
break;
case '"':
if (!first_quote)
first_quote = *string;
else {
/* Only accept close of string then \n */
if ((*string)[1] != '\n')
barf("String must end of end of line");
}
break;
default:
break;
}
(*string)++;
}
if (first_quote) {
/* Trim both quotes. */
if ((*string)[-1] != '"')
barf("Incomplete string");
(*string)[-1] = '\0';
memmove(first_quote, first_quote+1, strlen(first_quote));
} else
**string = '\0';
(*string)++;
return start;
}
static void wml_add(struct wml *wml, const char *line)
{
wml->keyval = realloc_array(wml->keyval, wml->num_keys+1);
wml->keyval[wml->num_keys++] = line;
}
static void wml_add_child(struct wml *wml, struct wml *child)
{
wml->child = realloc_array(wml->child, wml->num_children+1);
wml->child[wml->num_children++] = child;
}
#if 0 /* Unused */
static void wml_replace(struct wml *wml, const char *key, const char *value)
{
unsigned int i, len = strlen(key);
for (i = 0; i < wml->num_keys; i++) {
if (memcmp(wml->keyval[i], key, len) == 0
&& wml->keyval[i][len] == '=') {
wml->keyval[i] = aprintf("%s=%s", key, value);
return;
}
}
barf("Could not find key '%s' to replace", key);
}
#endif
static struct wml *parse_data(char **string, const char *name)
{
char *line;
char end[2 + strlen(name ? name : "") + 1 + 1];
struct wml *wml = new_wml(name);
if (name)
sprintf(end, "[/%s]", name);
else
end[0] = '\0';
for (;;) {
line = get_line(string);
if (!line) {
/* Only the top element can terminate this way. */
if (!name)
return wml;
barf("Unexpected end of file during [%s]", name);
}
/* The end? */
if (streq(line, end))
return wml;
/* Child? */
if (*line == '[') {
struct wml *child;
if (line[1] == '/' || !strends(line, "]"))
barf("Malformed line '%s' during [%s]",
line, name);
line[strlen(line) - 1] = '\0';
child = parse_data(string, line+1);
wml_add_child(wml, child);
} else {
char *eq = strchr(line, '=');
if (!eq)
barf("Expected '=' in '%s'", line);
wml_add(wml, line);
}
}
}
static bool write_all(int fd, const void *data, unsigned int len)
{
while (len) {
int done;
done = write(fd, data, len);
if (done < 0 && errno == EINTR)
continue;
if (done <= 0)
return false;
data += done;
len -= done;
}
return true;
}
/* Simple parser for Wesnoth Markup Language. */
static struct wml *parse(int fd)
{
unsigned long size;
char *data = grab_input(fd, &size);
logp("Content-length: %lu\n", size);
if (!write_all(log_fd, data, size))
barf("Could not write input to log");
/* No NULs please */
if (strlen(data) != size)
barf("Contained embedded NUL chars");
logp("=== END INPUT ===");
return parse_data(&data, NULL);
}
/* For debygging */
void dump(const struct wml *wml, unsigned int level);
void dump(const struct wml *wml, unsigned int level)
{
unsigned int i;
char indent[level+1];
memset(indent, '\t', level);
indent[level] = '\0';
for (i = 0; i < wml->num_keys; i++)
printf("%s%s\n", indent, wml->keyval[i]);
for (i = 0; i < wml->num_children; i++) {
printf("%s[%s]\n", indent, wml->child[i]->name);
dump(wml->child[i], level+1);
}
}
#define INTEGER 1
#define TEXT 2
#define NAME_REFERENCE 3
#define UNIQUE_TEXT 4
#define UNIQUE_INTEGER 5
/* We don't put common strings into tables, but instead use a reference into
* a table of names. */
static void __attribute__((sentinel))
create_table(void *h, const char *tablename, ...)
{
va_list ap;
const char *name;
char sep = '(';
char *cmd = aprintf("CREATE TABLE \"%s\" ", tablename);
va_start(ap, tablename);
while ((name = va_arg(ap, const char *)) != NULL) {
switch (va_arg(ap, int)) {
case INTEGER:
cmd = aprintf_add(cmd, "%c%s INTEGER", sep, name);
break;
case UNIQUE_INTEGER:
cmd = aprintf_add(cmd, "%c%s INTEGER UNIQUE",
sep, name);
break;
case TEXT:
cmd = aprintf_add(cmd, "%c%s TEXT", sep, name);
break;
case UNIQUE_TEXT:
cmd = aprintf_add(cmd, "%c%s TEXT UNIQUE",
sep, name);
break;
case NAME_REFERENCE: {
char tblname[strlen(name)+1];
cmd = aprintf_add(cmd, "%c%s INTEGER", sep, name);
memcpy(tblname, name, strlen(name) - strlen("_ref"));
tblname[strlen(name) - strlen("_ref")] = '\0';
strcat(tblname, "s");
create_table(h, tblname, "name", UNIQUE_TEXT, NULL);
break;
}
default:
barf("Unexpected type in create_table");
}
sep = ',';
}
va_end(ap);
cmd = aprintf_add(cmd, ");");
db_command(h, cmd);
}
static void create_tables(void *h)
{
do {
db_transaction_start(h);
create_table(h, "players",
"id", UNIQUE_TEXT,
NULL);
create_table(h, "game_count",
"player_ref", UNIQUE_INTEGER,
"games_received", INTEGER,
"last_upload", TEXT,
NULL);
create_table(h, "campaigns",
"player_ref", INTEGER,
"campaign_name_ref", NAME_REFERENCE,
"difficulty_name_ref", NAME_REFERENCE,
NULL);
create_table(h, "scenarios",
"campaign_ref", INTEGER,
"scenario_name_ref", NAME_REFERENCE,
NULL);
create_table(h, "games",
"scenario_ref", INTEGER,
"start_turn", INTEGER,
"gold", INTEGER,
"start_time", INTEGER,
"version_name_ref", NAME_REFERENCE,
"game_number", INTEGER,
/* 0 = unknown/quit, 1 = victory, 2 = defeat */
"result", INTEGER,
"end_time", INTEGER,
"end_turn", INTEGER,
"num_turns", INTEGER,
/* This is only set on victory. */
"end_gold", INTEGER,
NULL);
create_table(h, "special_units",
"game_ref", INTEGER,
"unit_name_ref", NAME_REFERENCE,
"level", INTEGER,
"experience", INTEGER,
NULL);
create_table(h, "unit_types",
"name", TEXT,
"level", INTEGER,
NULL);
create_table(h, "unit_tallies",
"game_ref", INTEGER,
"unit_type_ref", INTEGER,
"count", INTEGER,
NULL);
create_table(h, "serial",
"id", UNIQUE_TEXT,
NULL);
create_table(h, "bad_serial",
"id", UNIQUE_TEXT,
NULL);
db_command(h, "CREATE VIEW campaign_view AS SELECT games.rowid AS game,games.end_time-games.start_time AS time,scenario_names.name AS scenario,games.result AS result,games.start_turn AS start_turn,games.end_turn AS end_turn,games.num_turns AS num_turns,games.gold AS gold,players.id AS player,campaign_names.name AS campaign,difficulty_names.name AS difficulty,version_names.name AS version FROM games INNER JOIN scenarios ON games.scenario_ref = scenarios.rowid INNER JOIN campaigns ON scenarios.campaign_ref = campaigns.rowid INNER JOIN difficulty_names ON campaigns.difficulty_name_ref = difficulty_names.rowid INNER JOIN version_names ON games.version_name_ref = version_names.rowid INNER JOIN scenario_names ON scenarios.scenario_name_ref = scenario_names.rowid INNER JOIN campaign_names ON campaigns.campaign_name_ref = campaign_names.rowid INNER JOIN players ON players.rowid = campaigns.player_ref;");
db_command(h, "CREATE VIEW units_view AS SELECT scenario_names.name AS scenario,games.rowid AS game,unit_tallies.count AS count,unit_types.level AS level,players.id AS player,campaign_names.name AS campaign,difficulty_names.name AS difficulty,version_names.name AS version, games.start_turn AS start_turn FROM games INNER JOIN scenarios ON games.scenario_ref = scenarios.rowid INNER JOIN campaigns ON scenarios.campaign_ref = campaigns.rowid INNER JOIN difficulty_names ON campaigns.difficulty_name_ref = difficulty_names.rowid INNER JOIN version_names ON games.version_name_ref = version_names.rowid INNER JOIN unit_tallies ON games.rowid = unit_tallies.game_ref INNER JOIN unit_types ON unit_types.rowid = unit_tallies.unit_type_ref INNER JOIN scenario_names ON scenarios.scenario_name_ref = scenario_names.rowid INNER JOIN campaign_names ON campaigns.campaign_name_ref = campaign_names.rowid INNER JOIN players ON players.rowid = campaigns.player_ref;");
db_command(h, "CREATE UNIQUE INDEX unit_tallies_idx ON unit_tallies (game_ref, unit_type_ref, count);");
} while (!db_transaction_finish(h));
}
static const char *get_maybe(const struct wml *wml, const char *key)
{
unsigned int i, len = strlen(key);
for (i = 0; i < wml->num_keys; i++) {
if (memcmp(wml->keyval[i], key, len) == 0
&& wml->keyval[i][len] == '=')
return wml->keyval[i] + len + 1;
}
return NULL;
}
/* Sanity check that this really is an int. */
static const char *is_int(const char *val)
{
char *endp;
strtoul(val, &endp, 10);
if (*endp || endp == val)
barf("Value '%s' is not a valid integer", val);
return val;
}
static const char *get(const struct wml *wml, const char *key)
{
const char *ret = get_maybe(wml, key);
if (ret)
return ret;
if (!wml->name)
barf("Did not find toplevel key '%s'", key);
barf("Did not find key '%s' in [%s]", key, wml->name);
}
static struct wml *get_child(const struct wml *wml, const char *key)
{
unsigned int i;
for (i = 0; i < wml->num_children; i++) {
if (streq(wml->child[i]->name, key))
return wml->child[i];
}
return NULL;
}
/* Returns NULL or array of columns. */
static char **db_select(void *h, const char *table, const char *key,
const char *val, ...)
{
char *cmd;
const char *col;
char sep = ' ';
va_list ap;
struct db_query *query;
cmd = aprintf("SELECT");
va_start(ap, val);
while ((col = va_arg(ap, const char *)) != NULL) {
cmd = aprintf_add(cmd, "%c\"%s\"", sep, col);
sep = ',';
}
va_end(ap);
cmd = aprintf_add(cmd, " FROM \"%s\" WHERE \"%s\" = \"%s\";",
table, key, val);
query = db_query(h, cmd);
if (query->num_rows > 1)
barf_perror("Query '%s' returned %i rows",
cmd, query->num_rows);
return query->num_rows ? query->rows[0] : NULL;
}
static char *get_name_ref(void *h, const char *table, const char *name)
{
char **answer;
answer = db_select(h, table, "name", name, "ROWID", NULL);
if (!answer) {
char *cmd;
cmd = aprintf("INSERT INTO \"%s\" VALUES(\"%s\");",table,name);
db_command(h, cmd);
answer = db_select(h, table, "name", name, "ROWID", NULL);
if (!answer)
barf("Cannot find name '%s' after insert in table %s",
name, table);
}
return answer[0];
}
static char *get_unit_type_ref(void *h, const char *name, const char *level)
{
struct db_query *q;
char *query, *cmd;
query = aprintf("SELECT ROWID FROM \"unit_types\" where"
" \"name\" = \"%s\" AND \"level\" = \"%s\";",
name, level);
q = db_query(h, query);
if (q->num_rows)
return q->rows[0][0];
cmd = aprintf("INSERT INTO \"unit_types\" VALUES(\"%s\",\"%s\");",
name, level);
db_command(h, cmd);
q = db_query(h, query);
if (q->num_rows != 1)
barf("Cannot find unit_type '%s/%s' after insert",
name, level);
return q->rows[0][0];
}
/* Return ROWID of this entry (key, value) pairs. */
static char *make_ref(void *h, bool might_exist, const char *table, ...)
{
struct db_query *q;
char *query, *cmd;
const char *key, *val;
const char *sep = "";
va_list ap;
query = aprintf("SELECT ROWID FROM \"%s\" WHERE ", table);
va_start(ap, table);
while ((key = va_arg(ap, const char *)) != NULL) {
val = va_arg(ap, const char *);
query = aprintf_add(query, "%s\"%s\" = \"%s\"",
sep, key, val);
sep = " AND ";
}
va_end(ap);
query = aprintf_add(query, ";");
if (might_exist) {
/* Try looking for it first? */
q = db_query(h, query);
if (q->num_rows)
return q->rows[0][0];
}
/* Didn't find one, so make one. */
cmd = aprintf("INSERT INTO \"%s\" (", table);
sep = "";
va_start(ap, table);
while ((key = va_arg(ap, const char *)) != NULL) {
val = va_arg(ap, const char *);
cmd = aprintf_add(cmd, "%s\"%s\"", sep, key);
sep = ",";
}
va_end(ap);
cmd = aprintf_add(cmd, ") VALUES (");
sep = "";
va_start(ap, table);
while ((key = va_arg(ap, const char *)) != NULL) {
val = va_arg(ap, const char *);
cmd = aprintf_add(cmd, "%s\"%s\"", sep, val);
sep = ",";
}
va_end(ap);
cmd = aprintf_add(cmd, ");");
db_command(h, cmd);
/* FIXME: doesn't insert return the ROWID? */
q = db_query(h, query);
if (q->num_rows != 1)
barf("Entry '%s' did not exist after '%s'", query, cmd);
return q->rows[0][0];
}
/* [5]
[Elder Mage]
count="1"
[/Elder Mage]
[/5]
*/
static void add_tally_level(void *h, const char *game_ref, struct wml *level)
{
unsigned int i;
for (i = 0; i < level->num_children; i++) {
char *unit_type;
unit_type = get_unit_type_ref(h, level->child[i]->name,
is_int(level->name));
make_ref(h, false, "unit_tallies",
"game_ref", game_ref,
"unit_type_ref", unit_type,
"count", is_int(get(level->child[i],"count")),
NULL);
}
}
/*
[units-by-level]
[2] ...
[5] ...
[/units-by-level]
*/
static void add_tally(void *h, const char *game_ref, struct wml *tally)
{
unsigned int i;
if (!tally)
barf("No units-by-level entry");
for (i = 0; i < tally->num_children; i++)
add_tally_level(h, game_ref, tally->child[i]);
}
/* [special-unit]
experience="22"
level="3"
name="Konrad"
[/special-unit]
*/
static void add_special(void *h, const char *game_ref, struct wml *special)
{
char *name;
name = get_name_ref(h, "unit_names", get(special, "name"));
make_ref(h, false, "special_units",
"game_ref", game_ref,
"unit_name_ref", name,
"level", get(special, "level"),
"experience", get(special, "experience"),
NULL);
}
/* [game]
campaign="CAMPAIGN_HEIR_TO_THE_THRONE"
difficulty="NORMAL"
gold="549"
scenario="Mountain_Pass"
time="2052"
start_turn="10"
version="1.1-svn"
[special-unit]...
[units-by-level]...
One of:
[victory]
gold="749"
time="3351"
end_turn="19"
[/victory]
OR:
[defeat]
time="5003"
end_turn="19"
[/defeat]
OR:
[quit]
time="5003"
end_turn="19"
[/quit]
*/
static void add_game(void *h,
const char *player_ref,
const char *version_ref,
unsigned int gamenum,
struct wml *game)
{
struct wml *result;
const char *campaign, *difficulty, *scenario;
char *campaign_ref, *scenario_ref, *game_ref;
const char *gold, *start_time, *start_turn, *game_number, *result_num,
*end_time, *end_turn, *end_gold, *num_turns;
unsigned int i;
/* Get reference numbers for strings. */
campaign = get_name_ref(h, "campaign_names", get(game,"campaign"));
difficulty = get_name_ref(h,"difficulty_names",get(game,"difficulty"));
scenario = get_name_ref(h, "scenario_names", get(game,"scenario"));
/* Get reference for campaign entry. */
campaign_ref = make_ref(h, true, "campaigns",
"player_ref", player_ref,
"campaign_name_ref", campaign,
"difficulty_name_ref", difficulty, NULL);
/* Get reference for scenario. */
scenario_ref = make_ref(h, true, "scenarios",
"campaign_ref", campaign_ref,
"scenario_name_ref", scenario, NULL);
gold = is_int(get(game, "gold"));
start_time = is_int(get(game, "time"));
num_turns = is_int(get(game, "num_turns"));
/* We can tell between save at turn 1, and at end of last scenario.
* This matters: a save at turn 1 means maps is not random.
*/
start_turn = get_maybe(game, "start_turn");
if (!start_turn)
start_turn = "0";
is_int(start_turn);
game_number = aprintf("%i", gamenum);
if ((result = get_child(game, "victory")) != NULL) {
result_num = "1";
end_time = is_int(get(result, "time"));
end_turn = is_int(get(result, "end_turn"));
end_gold = is_int(get(result, "gold"));
} else if ((result = get_child(game, "defeat")) != NULL) {
result_num = "2";
end_time = is_int(get(result, "time"));
end_turn = is_int(get(result, "end_turn"));
end_gold = "0";
} else if ((result = get_child(game, "quit")) != NULL) {
result_num = "0";
end_time = is_int(get(result, "time"));
end_turn = is_int(get(result, "end_turn"));
end_gold = "0";
} else
barf("No victory, defeat or quit!");
/* Make entry for this game. */
game_ref = make_ref(h, false, "games",
"scenario_ref", scenario_ref,
"start_turn", start_turn,
"gold", gold,
"start_time", start_time,
"version_name_ref", version_ref,
"game_number", game_number,
"result", result_num,
"end_time", end_time,
"end_turn", end_turn,
"end_gold", end_gold,
"num_turns", num_turns,
NULL);
/* Make entry for each special-unit. */
for (i = 0; i < game->num_children; i++) {
if (streq(game->child[i]->name, "special-unit"))
add_special(h, game_ref, game->child[i]);
}
add_tally(h, game_ref, get_child(game, "units-by-level"));
}
static const char *find_or_create_player(void *h,
const char *id,
unsigned int *game_number)
{
char **answer, *cmd;
answer = db_select(h, "players", "id", id, "ROWID", NULL);
if (!answer) {
cmd = aprintf("INSERT INTO \"players\" VALUES(\"%s\");", id);
db_command(h, cmd);
answer = db_select(h, "players", "id", id, "ROWID", NULL);
*game_number = 1;
} else {
char **game;
game = db_select(h, "game_count", "player_ref", answer[0],
"games_received", NULL);
if (!game)
barf("No game_count entry for %s", answer[0]);
*game_number = atoi(game[0]);
}
return answer[0];
}
static void update_player_games(void *h,
const char *player_ref, unsigned int num)
{
char *cmd;
cmd = aprintf("INSERT OR REPLACE INTO \"game_count\""
" VALUES(\"%s\",%u,CURRENT_DATE);", player_ref, num);
db_command(h, cmd);
}
static void add_to_database(void *h, struct wml *wml)
{
const char *version_ref, *id, *serial;
const char *player_ref;
unsigned int i, games;
serial = get_maybe(wml, "serial");
id = get(wml, "id");
do {
db_transaction_start(h);
if (serial) {
/* We already have it. */
if (db_select(h, "serial", "id", serial, "ROWID",NULL))
return;
/* Manually-maintained list of known-bad files. */
if (db_select(h, "bad_serial", "id", serial, "ROWID",
NULL))
return;
make_ref(h, false, "serial", "id", serial, NULL);
}
version_ref = get_name_ref(h, "version_names",
get(wml, "version"));
player_ref = find_or_create_player(h, id, &games);
for (i = 0; i < wml->num_children; i++) {
if (!streq(wml->child[i]->name, "game"))
barf("Unexpected toplevel element [%s]",
wml->child[i]->name);
add_game(h, player_ref, version_ref, games + i,
wml->child[i]);
}
update_player_games(h, player_ref, games + i);
} while (!db_transaction_finish(h));
}
/* Convert game to latest version. */
static struct wml *convert_game(struct wml *game, const char *version)
{
return game;
}
/* Convert wml to the latest version. */
static struct wml *convert_wml(struct wml *wml, const char *version)
{
unsigned int i;
for (i = 0; i < wml->num_children; i++)
if (streq(wml->child[i]->name, "game"))
wml->child[i] = convert_game(wml->child[i], version);
return wml;
}
static void receive(int fd)
{
struct wml *wml;
char *logfile;
void *handle;
maybe_log_to_file(&logfile);
handle = db_open(database_file());
wml = parse(fd);
wml = convert_wml(wml, get(wml, "format_version"));
add_to_database(handle, wml);
/* We need this for a valid reply. */
printf("Content-type: text/plain\n\n");
if (logfile)
unlink(logfile);
db_close(handle);
}
/* For testing, takes a whole pile of files as input. */
int main(int argc, char *argv[])
{
if (argc == 2 && streq(argv[1], "--initialize")) {
create_tables(db_open(database_file()));
exit(0);
}
nice(10);
if (argc == 1)
receive(STDIN_FILENO);
else {
int i;
for (i = 1; i < argc; i++)
receive(open(argv[i], O_RDONLY));
}
return 0;
}

View file

@ -1,135 +0,0 @@
/* Random util functions, mainly taken from stdrusty.h */
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include "utils.h"
void *_realloc_array(void *ptr, size_t size, size_t num)
{
if (num >= SIZE_MAX/size)
return NULL;
return realloc_nofail(ptr, size * num);
}
void *realloc_nofail(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (ptr)
return ptr;
barf("realloc of %zu failed", size);
}
void *malloc_nofail(size_t size)
{
void *ptr = malloc(size);
if (ptr)
return ptr;
barf("malloc of %zu failed", size);
}
/* Avoid stupid sprintf-style non-value return. */
char *aprintf(const char *fmt, ...)
{
char *ret;
va_list arglist;
va_start(arglist, fmt);
vasprintf(&ret, fmt, arglist);
va_end(arglist);
return ret;
}
char *aprintf_add(char *s, const char *fmt, ...)
{
int oldlen, len;
va_list arglist;
va_start(arglist, fmt);
len = vsnprintf(NULL, 0, fmt, arglist);
va_end(arglist);
oldlen = (s ? strlen(s) : 0);
s = realloc(s, oldlen + len + 1);
va_start(arglist, fmt);
vsnprintf(s + oldlen, len + 1, fmt, arglist);
va_end(arglist);
return s;
}
void logpv(const char *fmt, va_list arglist)
{
char *str;
vasprintf(&str, fmt, arglist);
write(log_fd, str, strlen(str));
}
void logp(const char *fmt, ...)
{
va_list arglist;
va_start(arglist, fmt);
logpv(fmt, arglist);
write(log_fd, "\n", 1);
va_end(arglist);
}
void barf(const char *fmt, ...)
{
va_list arglist;
write(log_fd, "FATAL: ", strlen("FATAL: "));
va_start(arglist, fmt);
logpv(fmt, arglist);
write(log_fd, "\n", 1);
va_end(arglist);
exit(1);
}
void barf_perror(const char *fmt, ...)
{
int err = errno;
va_list arglist;
fprintf(stderr, "FATAL: ");
va_start(arglist, fmt);
logpv(fmt, arglist);
va_end(arglist);
logp(": %s", strerror(err));
exit(1);
}
/* This is all squid allows in a POST by default anyway. */
#define MAXIMUM_LEN 40000
/* This version adds one byte (for nul term) */
void *grab_input(int fd, unsigned long *size)
{
int ret;
void *buffer;
buffer = malloc(MAXIMUM_LEN+1);
*size = 0;
while ((ret = read(fd, buffer + *size, MAXIMUM_LEN - *size)) > 0) {
*size += ret;
if (*size == MAXIMUM_LEN)
barf("too much input");
}
if (ret < 0)
barf_perror("read failure");
((char *)buffer)[*size] = '\0';
return buffer;
}
void _delete_arr(void *p, unsigned len, unsigned off, unsigned num, size_t s)
{
assert(off + num <= len);
memmove(p + off*s, p + (off+num)*s, (len - (off+num))*s);
}

View file

@ -1,72 +0,0 @@
/* Random util functions, mainly taken from stdrusty.h */
#ifndef _UPLOAD_ANALYSIS_UTILS_H
#define _UPLOAD_ANALYSIS_UTILS_H
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
/* Is A == B ? */
#define streq(a,b) (strcmp((a),(b)) == 0)
/* Does A start with B ? */
#define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0)
/* Does A end in B ? */
static inline bool strends(const char *a, const char *b)
{
if (strlen(a) < strlen(b))
return false;
return streq(a + strlen(a) - strlen(b), b);
}
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define ___stringify(x) #x
#define __stringify(x) ___stringify(x)
/* Convenient wrappers for malloc and realloc. Use them. */
#define new(type) ((type *)malloc_nofail(sizeof(type)))
#define new_array(type, num) realloc_array((type *)0, (num))
#define realloc_array(ptr, num) ((__typeof__(ptr))_realloc_array((ptr), sizeof((*ptr)), (num)))
void *malloc_nofail(size_t size);
void *realloc_nofail(void *ptr, size_t size);
void *_realloc_array(void *ptr, size_t size, size_t num);
/* Avoid stupid sprintf-style */
char *aprintf(const char *fmt, ...) __attribute__((format(printf,1,2)));
char *aprintf_add(char *s, const char *fmt, ...)
__attribute__((format(printf,2,3)));
void logpv(const char *fmt, va_list arglist);
void logp(const char *fmt, ...) __attribute__((format(printf,1,2)));
void barf(const char *fmt, ...) __attribute__((noreturn, format(printf,1,2)));
void barf_perror(const char *fmt, ...)
__attribute__((noreturn, format(printf,1,2)));
void *grab_input(int fd, unsigned long *size);
extern int log_fd;
/* from the Linux Kernel:
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
/* Delete (by memmove) elements of an array */
#define delete_arr(p, len, off, num) \
_delete_arr((p), (len), (off), (num), sizeof(*p))
void _delete_arr(void *p, unsigned len, unsigned off, unsigned num, size_t s);
#endif /* _UPLOAD_ANALYSIS_UTILS_H */

View file

@ -1,203 +0,0 @@
== Welcome to Rails
Rails is a web-application and persistence framework that includes everything
needed to create database-backed web-applications according to the
Model-View-Control pattern of separation. This pattern splits the view (also
called the presentation) into "dumb" templates that are primarily responsible
for inserting pre-built data in between HTML tags. The model contains the
"smart" domain objects (such as Account, Product, Person, Post) that holds all
the business logic and knows how to persist themselves to a database. The
controller handles the incoming requests (such as Save New Account, Update
Product, Show Post) by manipulating the model and directing data to the view.
In Rails, the model is handled by what's called an object-relational mapping
layer entitled Active Record. This layer allows you to present the data from
database rows as objects and embellish these data objects with business logic
methods. You can read more about Active Record in
link:files/vendor/rails/activerecord/README.html.
The controller and view are handled by the Action Pack, which handles both
layers by its two parts: Action View and Action Controller. These two layers
are bundled in a single package due to their heavy interdependence. This is
unlike the relationship between the Active Record and Action Pack that is much
more separate. Each of these packages can be used independently outside of
Rails. You can read more about Action Pack in
link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
and your application name. Ex: rails myapp
(If you've downloaded Rails in a complete tgz or zip, this step is already done)
2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
3. Go to http://localhost:3000/ and get "Welcome aboard: Youre riding the Rails!"
4. Follow the guidelines to start developing your application
== Web Servers
By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise
Rails will use WEBrick, the webserver that ships with Ruby. When you run script/server,
Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures
that you can always get up and running quickly.
Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
More info at: http://mongrel.rubyforge.org
If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than
Mongrel and WEBrick and also suited for production use, but requires additional
installation and currently only works well on OS X/Unix (Windows users are encouraged
to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from
http://www.lighttpd.net.
And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby
web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not
for production.
But of course its also possible to run Rails on any platform that supports FCGI.
Apache, LiteSpeed, IIS are just a few. For more information on FCGI,
please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI
== Debugging Rails
Sometimes your application goes wrong. Fortunately there are a lot of tools that
will help you debug it and get it back on the rails.
First area to check is the application log files. Have "tail -f" commands running
on the server.log and development.log. Rails will automatically display debugging
and runtime information to these files. Debugging info will also be shown in the
browser on requests from 127.0.0.1.
You can also log your own messages directly into the log file from your code using
the Ruby logger class from inside your controllers. Example:
class WeblogController < ActionController::Base
def destroy
@weblog = Weblog.find(params[:id])
@weblog.destroy
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
end
end
The result will be a message in your log file along the lines of:
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
More information on how to use the logger is at http://www.ruby-doc.org/core/
Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
These two online (and free) books will bring you up to speed on the Ruby language
and also on programming in general.
== Debugger
Debugger support is available through the debugger command when you start your Mongrel or
Webrick server with --debugger. This means that you can break out of execution at any point
in the code, investigate and change the model, AND then resume execution! Example:
class WeblogController < ActionController::Base
def index
@posts = Post.find(:all)
debugger
end
end
So the controller will accept the action, run the first line, then present you
with a IRB prompt in the server window. Here you can do things like:
>> @posts.inspect
=> "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
#<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
>> @posts.first.title = "hello from a debugger"
=> "hello from a debugger"
...and even better is that you can examine how your runtime objects actually work:
>> f = @posts.first
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
>> f.
Display all 152 possibilities? (y or n)
Finally, when you're ready to resume execution, you enter "cont"
== Console
You can interact with the domain model by starting the console through <tt>script/console</tt>.
Here you'll have all parts of the application configured, just like it is when the
application is running. You can inspect domain models, change values, and save to the
database. Starting the script without arguments will launch it in the development environment.
Passing an argument will specify a different environment, like <tt>script/console production</tt>.
To reload your controllers and models after launching the console run <tt>reload!</tt>
== Description of Contents
app
Holds all the code that's specific to this particular application.
app/controllers
Holds controllers that should be named like weblogs_controller.rb for
automated URL mapping. All controllers should descend from ApplicationController
which itself descends from ActionController::Base.
app/models
Holds models that should be named like post.rb.
Most models will descend from ActiveRecord::Base.
app/views
Holds the template files for the view that should be named like
weblogs/index.erb for the WeblogsController#index action. All views use eRuby
syntax.
app/views/layouts
Holds the template files for layouts to be used with views. This models the common
header/footer method of wrapping views. In your views, define a layout using the
<tt>layout :default</tt> and create a file named default.erb. Inside default.erb,
call <% yield %> to render the view using this layout.
app/helpers
Holds view helpers that should be named like weblogs_helper.rb. These are generated
for you automatically when using script/generate for controllers. Helpers can be used to
wrap functionality for your views into methods.
config
Configuration files for the Rails environment, the routing map, the database, and other dependencies.
db
Contains the database schema in schema.rb. db/migrate contains all
the sequence of Migrations for your schema.
doc
This directory is where your application documentation will be stored when generated
using <tt>rake doc:app</tt>
lib
Application specific libraries. Basically, any kind of custom code that doesn't
belong under controllers, models, or helpers. This directory is in the load path.
public
The directory available for the web server. Contains subdirectories for images, stylesheets,
and javascripts. Also contains the dispatchers and the default HTML files. This should be
set as the DOCUMENT_ROOT of your web server.
script
Helper scripts for automation and generation.
test
Unit and functional tests along with fixtures. When using the script/generate scripts, template
test files will be generated for you and placed in this directory.
vendor
External libraries that the application depends on. Also includes the plugins subdirectory.
This directory is in the load path.

View file

@ -1,10 +0,0 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'tasks/rails'

View file

@ -1,10 +0,0 @@
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
# See ActionController::RequestForgeryProtection for details
# Uncomment the :secret if you're not using the cookie session store
protect_from_forgery # :secret => '978f5366084e76bc2bddc16fe1bd2897'
end

View file

@ -1,102 +0,0 @@
require 'campaign/sort.rb'
require 'campaign/campaign.rb'
class CampaignController < ApplicationController
# include lib campaign helper
include CampaignLib
def index
# retrieve elements
@name = params[:name] || nil
@difficulty = params[:diff] || nil
@version = params[:ver] || nil
# now we need to delegate to a proper handler
end
# '/campaign/about?...' handler (version name must be supplied)
def about
# make sure all necessary params are present
if params[:name].nil? || params[:diff].nil? || params[:ver].nil?
redirect_to :controller => 'version', :action => 'index'
return
end
# retrieve version_id, difficulty_id, and campaign_id
version_id = VersionName.find_by_name(params[:ver]).id
# retrieve campaign_id
campaign_id = CampaignName.find_by_name(params[:name]).id
# retrieve difficulty_id
difficulty_id = DifficultyName.find_by_name(params[:diff]).id
# grab all scenarios for the given campaign and apply scenario sorting
scenarios = scenario_sort(CampaignName.all_scenarios_for_campaign(params[:name]))
# create new campaign helper
@obj_campaign = Campaign.new
# store necessary info about this campaign
@obj_campaign.name = params[:name]
@obj_campaign.version = params[:ver]
@obj_campaign.difficulty = params[:diff]
# for every scenario grab all games
scenarios.each do |scenario|
# create new scenario
obj_scenario = Scenario.new
# store name
obj_scenario.name = scenario.name
# grab all games for this scenario, version, difficulty
arr_games = Game.all_games_for_campaign_version_difficulty_and_scenario(campaign_id, version_id, difficulty_id, scenario.id)
# iterate through games and store them in this scenario object
arr_games.each do |game|
# store game
obj_scenario.games << game
# for each game, calculate number of units of each level
arr_game_counts = []
# store counts for this game
obj_scenario.game_unit_counts << Unit.count_unit_levels(game.id)
end
# store scenario
@obj_campaign.scenarios << obj_scenario
end
# compute necessary stats
@obj_campaign.compute_stats
# Wins (across all scenarios)
# Percent turns used on victory (across all scenarios)
# Total minutes per player (across all scenarios)
# Count of Level 1 units at start of game (across all scenarios)
# Count of Level 2 units at start of game (across all scenarios)
# Count of Level 3 units at start of game (across all scenarios)
end
end

View file

@ -1,151 +0,0 @@
require 'version/branch'
class VersionController < ApplicationController
# general '/version' handler
#def index
#
# # redirect to 'list_all'
# redirect_to :action => 'list_all'
#end
# '/version/list_all' handler
# provides a generic listing of branches / versions with links
def index
# reset branch array
@arr_branches = []
# grab a sorted list of all versions
# group versions by branch i.e. (1.4.0 and 1.4.1 are grouped under 1.4)
hash_versions = VersionName.all_branches
# iterate through hash
hash_versions.each do |iter_branch, iter_versions|
# create new branch
obj_branch = Version::Branch.new
# store name of this branch
obj_branch.name = iter_branch
# iterate through all versions in this branch
iter_versions.each do |iter_version|
# create new version
obj_version = Version::Version.new
# store version name
obj_version.name = iter_version.name
# retrieve and store number of games for this version
obj_version.game_count = Game.count_games_for_version(iter_version.id)
#obj_version.game_count = iter_version.games.size
# increment branch game count
obj_branch.game_count += obj_version.game_count
# store version for in this branch
obj_branch.versions << obj_version
end
# index versions in this branch
obj_branch.index_versions_by_subversion
# store branch
@arr_branches << obj_branch
end
end
# '/version/about/<name>' handler (version name must be supplied)
def about
# make sure we have 'name' param present and is valid
if params[:name].nil?
redirect_to :action => 'index'
return
end
# find version with the given name (we need id)
hash_version = VersionName.find_by_name(params[:name])
if hash_version.nil?
redirect_to :action => 'index'
return
end
# make sure we have 'name' is valid/present
#begin
#
# hash_version = VersionNames.find_by_name(params[:name])
#
# logger.info("--")
# logger.info(hash_version)
#
#rescue ActiveRecord::RecordNotFound
#
# # redirect to 'list_all' if missing the 'id'
# redirect_to :action => 'list_all'
# return
#end
# grab a sorted list of all campaigns
hash_campaigns = CampaignName.all_order_by_name
# create new version
@obj_version = Version::Version.new
# store version name
@obj_version.name = hash_version.name
# iterate through hash
hash_campaigns.each do |iter_campaign|
# calculate how many games for this version and this campaign
game_count = Game.count_games_for_campaign_and_version(iter_campaign.id, hash_version.id)
if game_count > 0
# create new campaign
obj_campaign = Version::Campaign.new
# store game count, name
obj_campaign.game_count = game_count
obj_campaign.name = iter_campaign.name
# increment game count for this version
@obj_version.game_count += obj_campaign.game_count
# now we need to retrieve difficulties and game counts for this campaign
hash_difficulties = DifficultyName.all_order_by_name
# iterate through hash
hash_difficulties.each do |iter_difficulty|
# create new difficulty
obj_difficulty = Version::Difficulty.new
# calculate how many games for this version, difficulty and this campaign
game_count = Game.count_games_for_campaign_version_and_difficulty(iter_campaign.id, hash_version.id, iter_difficulty.id)
# store game count, name
obj_difficulty.game_count = game_count
obj_difficulty.name = iter_difficulty.name
# store difficulty for this campaign
obj_campaign.difficulties << obj_difficulty
end
# store this campaign
@obj_version.campaigns << obj_campaign
end
end
end
end

View file

@ -1,4 +0,0 @@
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
end

View file

@ -1,3 +0,0 @@
module CampaignHelper
end

View file

@ -1,3 +0,0 @@
module VersionHelper
end

View file

@ -1,33 +0,0 @@
class CampaignName < ActiveRecord::Base
# there are multiple games for each campaign name
has_many :games
# there are multiple scenarios for this campaign name
has_many :scenario_names, :through => :games
# Return all campaigns, ordered by name
def self.all_order_by_name
CampaignName.find :all, :order => "name ASC"
end
# find all scenarios for a given campaign
def self.all_scenarios_for_campaign(campaign_name)
#CampaignName.find_all_by_name campaign_name,
# :first,
# :select => "scenario_names.name",
# :include => [:scenario_names]
CampaignName.find( :first,
:conditions => ["campaign_names.name = :campaign_name", {:campaign_name => campaign_name}],
:select => "scenario_names.*",
:include => [:scenario_names]).scenario_names
end
end

View file

@ -1,10 +0,0 @@
class DifficultyName < ActiveRecord::Base
# there are multiple games present for each difficulty name
has_many :games
def self.all_order_by_name
DifficultyName.find :all, :order => "name ASC"
end
end

View file

@ -1,131 +0,0 @@
class Game < ActiveRecord::Base
# game has a particular version
belongs_to :version_name
# game has a particular campaign
belongs_to :campaign_name
# game has a particular scenario
belongs_to :scenario_name
# game belongs to a particular difficulty
#belongs_to :difficulty_name#, :class_name => "DifficultyNames", :foreign_key => "difficulty_name_id"
# there are multiple games present for each unit
has_many :units
# there are multiple games present for each special_unit
has_many :special_units
# count how many games of a particular version there are
def self.count_games_for_version(version_id)
Game.count :conditions => ["version_name_id = :version_id", {:version_id => version_id}]
end
# count how many games for a particular campaign there are
def self.count_games_for_campaign(campaign_id)
Game.count :conditions => ["campaign_name_id = :campaign_id", {:campaign_id => campaign_id}]
end
# count how many games for a particular campaign and version there is
def self.count_games_for_campaign_and_version(campaign_id, version_id)
Game.count :conditions => ["campaign_name_id = :campaign_id AND version_name_id = :version_id", {:campaign_id => campaign_id, :version_id => version_id}]
end
# count how many games for a particular campaign, version and difficulty there is
def self.count_games_for_campaign_version_and_difficulty(campaign_id, version_id, difficulty_id)
Game.count :conditions => ["campaign_name_id = :campaign_id AND version_name_id = :version_id AND difficulty_name_id = :difficulty_id", {:campaign_id => campaign_id, :version_id => version_id, :difficulty_id => difficulty_id}]
end
# retrieve all games with given
def self.all_games_for_campaign_version_difficulty_and_scenario(campaign_id, version_id, difficulty_id, scenario_id)
Game.find :all,
:conditions => ["campaign_name_id = :campaign_id AND version_name_id = :version_id AND scenario_name_id = :scenario_id AND difficulty_name_id = :difficulty_id", {:campaign_id => campaign_id, :version_id => version_id, :scenario_id => scenario_id, :difficulty_id => difficulty_id}]
end
# find all unique scenario names for given campaign
#def self.find_scenarios_for_campaign(name)
#
# # CampaignName.find_by_name(params[:name]).scenario_names.uniq
# Game.find :all,
# :select => "DISTINCT scenario_names.name",
# :conditions => ["campaign_names.name = :name AND games.campaign_name_id = campaign_names.id AND games.scenario_name_id = scenario_names.id", {:name => name}],
# :include => [:campaign_name, :scenario_name]
#
#end
#def self.bah(version_id)
#
# Games.find( :all,
# :conditions => ["version_name_id = :version_id", {:version_id => version_id}],
# :select => "version_names.id, games.id",
# :include => [:version_name]#[:campaign_name, :version_name]
# )
#
#end
#
#def self.find_games_for_version(version_id)
#
# Games.find( :all,
# :conditions => ["version_name_id = :version_id", {:version_id => version_id}],
# :select => "
# )
#
#end
# count how many games for each version there is
#def self.count_games_for_each_version
#
# #Games.count(
# # :joins => "JOIN version_names on version_name_id = version_names.id",
# # :group => "version_name_id",
# # :order => "version_names.name"
# # )
#
# Games.find( :all,
# :joins => "JOIN version_names on version_name_id = version_names.id",
# #:select => "version_names.id, version_names.name, COUNT(*) as 'game_count'",
# :select => "version_names.name, COUNT(*) as 'game_count'",
# :group => "version_name_id",
# :order => "version_names.name"
# #:offset => offset,
# #:limit => limit
# )
#end
# count how many games for each branch there is
#def self.count_games_for_branch(branch)
#
# branch_query = branch + "%"
#
# Games.find( :all,
# :conditions => ["version_names.name LIKE :branch_query", {:branch_query => branch_query}],
# :joins => "JOIN version_names on version_name_id = version_names.id",
# :select => "COUNT(*) as 'game_count'"
# #:group => "version_name_id",
# #:order => "version_names.name"
# )
#
#end
end

View file

@ -1,2 +0,0 @@
class Player < ActiveRecord::Base
end

View file

@ -1,6 +0,0 @@
class ScenarioName < ActiveRecord::Base
# there are multiple games for each scenario
has_many :games
end

View file

@ -1,9 +0,0 @@
class SpecialUnit < ActiveRecord::Base
# special unit belongs to a particular game
belongs_to :game
# special unit belongs to a particular special_unit_name
belongs_to :special_unit_name
end

View file

@ -1,5 +0,0 @@
class SpecialUnitName < ActiveRecord::Base
# there are multiple special units for each special_unit_name
has_many :special_units
end

View file

@ -1,25 +0,0 @@
class Unit < ActiveRecord::Base
# unit belongs to a particular game
belongs_to :game
# unit belongs to a particular unit_name
belongs_to :unit_name
# count how many units of a given level there are for a given game
#def self.count_units_of_level(game_id, level)
#
# Unit.sum :count,
# :conditions => ["game_id = :game_id AND level = :level", {:game_id => game_id, :level => level}]
#end
# return unit counts for each level
def self.count_unit_levels(game_id)
Unit.find :all,
:select => "level, SUM(count) AS level_count",
:conditions => ["game_id = :game_id", {:game_id => game_id}],
:group => 'level'
end
end

View file

@ -1,5 +0,0 @@
class UnitName < ActiveRecord::Base
# there are multiple units for each unit_name
has_many :units
end

View file

@ -1,27 +0,0 @@
class VersionName < ActiveRecord::Base
# there are multiple games for each version
has_many :games
# count how many different versions there are
def self.count_versions
VersionName.count
end
# return all branches
def self.all_branches
VersionName.all_order_by_name.group_by { |version| version.name[0, 3] }
#VersionName.all.group_by { |version| version.name[0, 3] }
end
# return all versions ordered by name
def self.all_order_by_name
VersionName.find :all, :order => "name DESC"
end
end

View file

@ -1,2 +0,0 @@
%img{ :src => count_units_level_1 }
%p

View file

@ -1,2 +0,0 @@
%img{ :src => count_units_level_2 }
%p

View file

@ -1,2 +0,0 @@
%img{ :src => count_units_level_3 }
%p

View file

@ -1,2 +0,0 @@
%img{ :src => gold_start_game }
%p

View file

@ -1,2 +0,0 @@
%img{ :src => victory_percent_turns }
%p

View file

@ -1,2 +0,0 @@
%img{ :src => wins_losses_quits }
%p

View file

@ -1,42 +0,0 @@
%center
Campaign:
= @obj_campaign.name
Difficulty:
= @obj_campaign.difficulty
Version:
= @obj_campaign.version
%p
Gold at start of game:
%p
%p
= render :partial => 'gold_start_game', :collection => @obj_campaign.create_charts_gold_start_game
Wins, Losses, Quits percentage:
%p
%p
= render :partial => 'wins_losses_quits', :collection => @obj_campaign.create_charts_wins_losses_quits
Percent turns used on victory:
%p
%p
= render :partial => 'victory_percent_turns', :collection => @obj_campaign.create_charts_victory_percent_turns
Count of level 1 units:
%p
%p
= render :partial => 'count_units_level_1', :collection => @obj_campaign.create_charts_unit_count(1)
Count of level 2 units:
%p
%p
= render :partial => 'count_units_level_2', :collection => @obj_campaign.create_charts_unit_count(2)
Count of level 3 units:
%p
%p
= render :partial => 'count_units_level_3', :collection => @obj_campaign.create_charts_unit_count(3)

View file

@ -1,13 +0,0 @@
Branch:
= branch.name
Number of games:
= branch.game_count
%p
= render :partial => 'branch_version', :collection => branch.versions
%img{ :src => branch.create_branch_chart }/
%p

View file

@ -1,9 +0,0 @@
Version:
= branch_version.name
Number of games:
= branch_version.game_count
Link:
= link_to "version_link", {:action => "about", :name => branch_version.name}
%p

View file

@ -1,15 +0,0 @@
Campaign:
= campaign.name
Number of games:
= campaign.game_count
/Link:
/= link_to "campaign_link", {:controller => "campaign", :action => "about", :name => campaign.name, :diff => }
%p
= render :partial => 'campaign_difficulty', :collection => campaign.difficulties, :locals => { :campaign_name => campaign.name, :version_name => version_name}
%img{ :src => campaign.create_difficulty_chart }/
%p

View file

@ -1,10 +0,0 @@
Difficulty:
= campaign_difficulty.name
Number of games:
= campaign_difficulty.game_count
Link:
= link_to "campaign_link", {:controller => "campaign", :action => "about", :name => campaign_name, :diff => campaign_difficulty.name, :ver => version_name}
%p

View file

@ -1,9 +0,0 @@
Version:
= @obj_version.name
Game count:
= @obj_version.game_count
%p
.branch-section= render :partial => 'campaign', :collection => @obj_version.campaigns, :locals => { :version_name => @obj_version.name }

View file

@ -1,2 +0,0 @@
.branch-section= render :partial => 'branch', :collection => @arr_branches

View file

@ -1,109 +0,0 @@
# Don't change this file!
# Configure your app in config/environment.rb and config/environments/*.rb
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
module Rails
class << self
def boot!
unless booted?
preinitialize
pick_boot.run
end
end
def booted?
defined? Rails::Initializer
end
def pick_boot
(vendor_rails? ? VendorBoot : GemBoot).new
end
def vendor_rails?
File.exist?("#{RAILS_ROOT}/vendor/rails")
end
# FIXME : Ruby 1.9
def preinitialize
load(preinitializer_path) if File.exists?(preinitializer_path)
end
def preinitializer_path
"#{RAILS_ROOT}/config/preinitializer.rb"
end
end
class Boot
def run
load_initializer
Rails::Initializer.run(:set_load_path)
end
end
class VendorBoot < Boot
def load_initializer
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
end
end
class GemBoot < Boot
def load_initializer
self.class.load_rubygems
load_rails_gem
require 'initializer'
end
def load_rails_gem
if version = self.class.gem_version
gem 'rails', version
else
gem 'rails'
end
rescue Gem::LoadError => load_error
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
exit 1
end
class << self
def rubygems_version
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
end
def gem_version
if defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION
elsif ENV.include?('RAILS_GEM_VERSION')
ENV['RAILS_GEM_VERSION']
else
parse_gem_version(read_environment_rb)
end
end
def load_rubygems
require 'rubygems'
unless rubygems_version >= '0.9.4'
$stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1
end
rescue LoadError
$stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
exit 1
end
def parse_gem_version(text)
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
end
private
def read_environment_rb
File.read("#{RAILS_ROOT}/config/environment.rb")
end
end
end
end
# All that for this:
Rails.boot!

View file

@ -1,27 +0,0 @@
development:
adapter: mysql
database: wesnoth
host: <my_host_name>
port: 3306
username: <my_user_name>
password: <my_pswd>
timeout: 5000
test:
adapter: mysql
database: wesnoth
host: <my_host_name>
port: 3306
username: <my_user_name>
password: <my_pswd>
timeout: 5000
production:
adapter: mysql
database: wesnoth
host: <my_host_name>
port: 3306
username: <my_user_name>
password: <my_pswd>
timeout: 5000

View file

@ -1,62 +0,0 @@
# Be sure to restart your server when you modify this file
# Uncomment below to force Rails into production mode when
# you don't control web/app server and can't set it the proper way
ENV['RAILS_ENV'] ||= 'production'
# YOU MUST BE IN PRODUCTION MODE AT ALL TIMES
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.1' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
require 'gchart'
Rails::Initializer.run do |config|
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# See Rails::Configuration for more options.
# Skip frameworks you're not going to use (only works if using vendor/rails).
# To use Rails without a database, you must remove the Active Record framework
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
# Only load the plugins named here, in the order given. By default, all plugins
# in vendor/plugins are loaded in alphabetical order.
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/extras )
# Force all environments to use the same logger level
# (by default production uses :info, the others :debug)
# config.log_level = :debug
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
config.action_controller.session = {
:session_key => '_wesnoth_session',
:secret => '8da8b64e598afc43369c14b4abdd5ee3942228eab2c41ab47549c89b543f69d6ef6a0ab17d85062beb6c42bc7c9d9bc59a640016695ed0bceff8cf41b747d28f'
}
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with 'rake db:sessions:create')
# config.action_controller.session_store = :active_record_store
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector
# Make Active Record use UTC-base instead of local time
# config.active_record.default_timezone = :utc
end

View file

@ -1,18 +0,0 @@
# Settings specified here will take precedence over those in config/environment.rb
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes = false
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_view.debug_rjs = true
config.action_controller.perform_caching = false
config.action_view.cache_template_extensions = false
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false

View file

@ -1,19 +0,0 @@
# Settings specified here will take precedence over those in config/environment.rb
# The production environment is meant for finished, "live" apps.
# Code is not reloaded between requests
config.cache_classes = true
# Use a different logger for distributed setups
# config.logger = SyslogLogger.new
# Full error reports are disabled and caching is turned on
config.action_controller.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.action_view.cache_template_loading = true
# Enable serving of images, stylesheets, and javascripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false

View file

@ -1,22 +0,0 @@
# Settings specified here will take precedence over those in config/environment.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell ActionMailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test

View file

@ -1,10 +0,0 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format
# (all these examples are active by default):
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end

View file

@ -1,5 +0,0 @@
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone

View file

@ -1,15 +0,0 @@
# These settins change the behavior of Rails 2 apps and will be defaults
# for Rails 3. You can remove this initializer when Rails 3 is released.
# Only save the attributes that have changed since the record was loaded.
ActiveRecord::Base.partial_updates = true
# Include ActiveRecord class name as root for JSON serialized output.
ActiveRecord::Base.include_root_in_json = true
# Use ISO 8601 format for JSON serialized times and dates
ActiveSupport.use_standard_json_time_format = true
# Don't escape HTML entities in JSON, leave that for the #json_escape helper
# if you're including raw json in an HTML page.
ActiveSupport.escape_html_entities_in_json = false

View file

@ -1,42 +0,0 @@
ActionController::Routing::Routes.draw do |map|
# The priority is based upon order of creation: first created -> highest priority.
# Sample of regular route:
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
# map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
# This route can be invoked with purchase_url(:id => product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# map.resources :products
# Sample resource route with options:
# map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
# Sample resource route with sub-resources:
# map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
# Sample resource route within a namespace:
# map.namespace :admin do |admin|
# # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
# admin.resources :products
# end
# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
# map.root :controller => "welcome"
# See how all your routes lay out with "rake routes"
# Install the default routes as the lowest priority.
# Routing for version
map.connect 'version', :controller => 'version'
map.connect 'version/about/:name', :controller => 'version', :action => 'about', :requirements => { :name => /.*/ }
# Default routing
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end

View file

@ -1,18 +0,0 @@
class CreateCampaignNames < ActiveRecord::Migration
def self.up
create_table :campaign_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE campaign_names ENGINE = MyISAM'
end
def self.down
drop_table :campaign_names
end
end

View file

@ -1,18 +0,0 @@
class CreateDifficultyNames < ActiveRecord::Migration
def self.up
create_table :difficulty_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE difficulty_names ENGINE = MyISAM'
end
def self.down
drop_table :difficulty_names
end
end

View file

@ -1,18 +0,0 @@
class CreateScenarioNames < ActiveRecord::Migration
def self.up
create_table :scenario_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE scenario_names ENGINE = MyISAM'
end
def self.down
drop_table :scenario_names
end
end

View file

@ -1,18 +0,0 @@
class CreatePlayers < ActiveRecord::Migration
def self.up
create_table :players do |t|
t.column :player_id, :integer
end
# switch to 'MyISAM'
execute 'ALTER TABLE players ENGINE = MyISAM'
end
def self.down
drop_table :players
end
end

View file

@ -1,21 +0,0 @@
class CreateSpecialUnits < ActiveRecord::Migration
def self.up
create_table :special_units do |t|
t.column :game_id, :integer
t.column :level, :integer
t.column :special_unit_id, :integer
t.column :experience, :integer
end
# switch to 'MyISAM'
execute 'ALTER TABLE special_units ENGINE = MyISAM'
end
def self.down
drop_table :special_units
end
end

View file

@ -1,18 +0,0 @@
class CreateSpecialUnitNames < ActiveRecord::Migration
def self.up
create_table :special_unit_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE special_unit_names ENGINE = MyISAM'
end
def self.down
drop_table :special_unit_names
end
end

View file

@ -1,21 +0,0 @@
class CreateUnits < ActiveRecord::Migration
def self.up
create_table :units do |t|
t.column :game_id, :integer
t.column :level, :integer
t.column :unit_id, :integer
t.column :count, :integer
end
# switch to 'MyISAM'
execute 'ALTER TABLE units ENGINE = MyISAM'
end
def self.down
drop_table :units
end
end

View file

@ -1,18 +0,0 @@
class CreateUnitNames < ActiveRecord::Migration
def self.up
create_table :unit_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE unit_names ENGINE = MyISAM'
end
def self.down
drop_table :unit_names
end
end

View file

@ -1,18 +0,0 @@
class CreateVersionNames < ActiveRecord::Migration
def self.up
create_table :version_names do |t|
t.column :name, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE version_names ENGINE = MyISAM'
end
def self.down
drop_table :version_names
end
end

View file

@ -1,31 +0,0 @@
class CreateGames < ActiveRecord::Migration
def self.up
create_table :games do |t|
t.column :player_id, :integer
t.column :version_id, :integer
t.column :campaign_id, :integer
t.column :scenario_id, :integer
t.column :difficulty_id, :integer
t.column :number_turns, :integer
t.column :start_turn, :integer
t.column :end_turn, :integer
t.column :start_time, :integer
t.column :end_time, :integer
t.column :gold, :integer
t.column :end_gold, :integer
t.column :status, :integer
t.column :serial, :string
end
# switch to 'MyISAM'
execute 'ALTER TABLE games ENGINE = MyISAM'
end
def self.down
drop_table :games
end
end

View file

@ -1,19 +0,0 @@
class ModifyPlayersPlayerId < ActiveRecord::Migration
def self.up
# fix a bug, player_id is supposed to be a string
# also rename it, since it no longer will be used in indexing
change_column :players, :player_id, :string
rename_column :players, :player_id, :unique_player_id
end
def self.down
# can't go back
raise ActiveRecord::IrreversibleMigration
end
end

View file

@ -1,15 +0,0 @@
class ModifyPlayersUniqueIdLength < ActiveRecord::Migration
def self.up
# make unique_id max length equal to 30
change_column :players, :unique_player_id, :string, :limit => 30
end
def self.down
# remove unique_id length limit
change_column :players, :unique_player_id, :string
end
end

View file

@ -1,15 +0,0 @@
class ModifyGamesSerialLength < ActiveRecord::Migration
def self.up
# make serial max length equal to 30
change_column :games, :serial, :string, :limit => 30
end
def self.down
# remove serial length limit
change_column :games, :serial, :string
end
end

View file

@ -1,16 +0,0 @@
class ModifyGamesVersionNameId < ActiveRecord::Migration
def self.up
# rename version_id to version_name_id
rename_column :games, :version_id, :version_name_id
end
def self.down
# rename version_name_id to version_id
rename_column :games, :version_name_id, :version_id
end
end

View file

@ -1,16 +0,0 @@
class ModifyGamesCampaignNameId < ActiveRecord::Migration
def self.up
# rename campaign_id to campaign_name_id
rename_column :games, :campaign_id, :campaign_name_id
end
def self.down
# rename campaign_name_id to campaign_id
rename_column :games, :campaign_name_id, :campaign_id
end
end

View file

@ -1,16 +0,0 @@
class ModifyGamesScenarioNameId < ActiveRecord::Migration
def self.up
# rename scenario_id to scenario_name_id
rename_column :games, :scenario_id, :scenario_name_id
end
def self.down
# rename scenario_name_id to scenario_id
rename_column :games, :scenario_name_id, :scenario_id
end
end

View file

@ -1,15 +0,0 @@
class ModifyGamesDifficultyNameId < ActiveRecord::Migration
def self.up
# rename difficulty_id to difficulty_name_id
rename_column :games, :difficulty_id, :difficulty_name_id
end
def self.down
# rename difficulty_name_id to difficulty_id
rename_column :games, :difficulty_name_id, :difficulty_id
end
end

View file

@ -1,15 +0,0 @@
class ModifyUnitsUnitNameId < ActiveRecord::Migration
def self.up
# rename unit_id to unit_name_id
rename_column :units, :unit_id, :unit_name_id
end
def self.down
# rename unit_name_id to unit_id
rename_column :units, :unit_name_id, :unit_id
end
end

View file

@ -1,15 +0,0 @@
class ModifySpecialUnitsSpecialUnitNameId < ActiveRecord::Migration
def self.up
# rename special_unit_id to special_unit_name_id
rename_column :special_units, :special_unit_id, :special_unit_name_id
end
def self.down
# rename special_unit_name_id to special_unit_id
rename_column :special_units, :special_unit_name_id, :special_unit_id
end
end

View file

@ -1,73 +0,0 @@
# This file is auto-generated from the current state of the database. Instead of editing this file,
# please use the migrations feature of Active Record to incrementally modify your database, and
# then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
# to create the application database on another system, you should be using db:schema:load, not running
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 19) do
create_table "campaign_names", :force => true do |t|
t.string "name"
end
create_table "difficulty_names", :force => true do |t|
t.string "name"
end
create_table "games", :force => true do |t|
t.integer "player_id", :limit => 11
t.integer "version_name_id", :limit => 11
t.integer "campaign_name_id", :limit => 11
t.integer "scenario_name_id", :limit => 11
t.integer "difficulty_name_id", :limit => 11
t.integer "number_turns", :limit => 11
t.integer "start_turn", :limit => 11
t.integer "end_turn", :limit => 11
t.integer "start_time", :limit => 11
t.integer "end_time", :limit => 11
t.integer "gold", :limit => 11
t.integer "end_gold", :limit => 11
t.integer "status", :limit => 11
t.string "serial", :limit => 30
end
create_table "players", :force => true do |t|
t.string "unique_player_id", :limit => 30
end
create_table "scenario_names", :force => true do |t|
t.string "name"
end
create_table "special_unit_names", :force => true do |t|
t.string "name"
end
create_table "special_units", :force => true do |t|
t.integer "game_id", :limit => 11
t.integer "level", :limit => 11
t.integer "special_unit_name_id", :limit => 11
t.integer "experience", :limit => 11
end
create_table "unit_names", :force => true do |t|
t.string "name"
end
create_table "units", :force => true do |t|
t.integer "game_id", :limit => 11
t.integer "level", :limit => 11
t.integer "unit_name_id", :limit => 11
t.integer "count", :limit => 11
end
create_table "version_names", :force => true do |t|
t.string "name"
end
end

View file

@ -1,2 +0,0 @@
Use this README file to introduce your application and point to useful places in the API for learning more.
Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.

View file

@ -1,317 +0,0 @@
require 'campaign/scenario'
require 'googlechart/chart_bar'
require 'googlechart/chart_xy'
module CampaignLib
include GoogleChartLib
# Represents a campaign instance, with a set of scenarios
class Campaign
attr_accessor :scenarios, :name, :difficulty, :version
def initialize
@scenarios = []
@scenario_blocks = []
@name = ""
@difficulty = ""
@version = ""
end
# compute stats for this campaign
def compute_stats
# compute stats for each scenario
@scenarios.each do |scenario|
# compute gold at the start of the game (across all scenarios)
scenario.compute_start_gold
# compute Losses/quits (across all scenarios)
scenario.compute_wins_losses_quits
# compute victory percent turns (across all scenarios)
scenario.compute_victory_percent_turns
# compute unit counts
scenario.compute_unit_counts
end
# partition scenarios into blocks, so we can generate charts for each block
create_scenario_blocks
end
# create chart for unit count (given level)
def create_charts_unit_count(level)
# we are going to generate an array of charts showing
# unit counts for the given level
arr_charts = []
# for each block generate a chart
@scenario_blocks.each do |scenario_block|
arr_data = []
sym_level = "level_#{level}".to_sym
# store means and medians
arr_data << scenario_block.map { |scenario| scenario.stat_unit_counts[sym_level].mean }
arr_data << scenario_block.map { |scenario| scenario.stat_unit_counts[sym_level].median }
# store names
arr_labels = []
scenario_block.each do |scenario|
if scenario.name.length > 20
arr_labels << scenario.name[0, 20] + ".."
else
arr_labels << scenario.name
end
end
#scenario_block.map { |scenario| scenario.name[0, 20] }
obj_chart = chart_xy :title => "Count of level #{level} units: #{arr_labels.first} - #{arr_labels.last}",
:size => [700, 200],
:labels => arr_labels,
:data => arr_data,
:legend => ['Mean', 'Median'],
:colors => ['0033FF', 'FF6600'],
:legend_position => :top
# store chart (unit counts), median and mode
arr_charts << obj_chart
arr_data = []
# store max and mode
arr_data << scenario_block.map { |scenario| scenario.stat_unit_counts[sym_level].max }
arr_data << scenario_block.map { |scenario| scenario.stat_unit_counts[sym_level].mode }
obj_chart = chart_xy :title => "Count of level #{level} units: #{arr_labels.first} - #{arr_labels.last}",
:size => [700, 200],
:labels => arr_labels,
:data => arr_data,
:legend => ['Maximum', 'Mode'],
:colors => ['990000', '007700'],
:legend_position => :top
# store chart (unit counts), median and mode
arr_charts << obj_chart
end
return arr_charts
end
# create chart for number of turns % wins
def create_charts_victory_percent_turns
# we are going to generate an array of charts showing
# victory percent turns
arr_charts = []
# for each block generate a chart
@scenario_blocks.each do |scenario_block|
arr_data = []
# store means and medians
arr_data << scenario_block.map { |scenario| scenario.victory_percent_turns.mean }
arr_data << scenario_block.map { |scenario| scenario.victory_percent_turns.median }
# store names
#arr_labels = scenario_block.map { |scenario| scenario.name[0, 20] }
# store names
arr_labels = []
scenario_block.each do |scenario|
if scenario.name.length > 20
arr_labels << scenario.name[0, 20] + ".."
else
arr_labels << scenario.name
end
end
# create chart
obj_chart = chart_xy :title => "Percent turns used on victory: #{arr_labels.first} - #{arr_labels.last}",
:size => [700, 200],
:labels => arr_labels,
:data => arr_data,
:legend => ['Mean', 'Median'],
:colors => ['006400', 'FF6600'],
:legend_position => :top
# store chart
arr_charts << obj_chart
end
return arr_charts
end
# create chart for gold at the start of the game
def create_charts_gold_start_game
# we are going to generate an array of charts showing
# gold at the start of the game
arr_charts = []
# for each block (batch) generate a chart
@scenario_blocks.each do |scenario_block|
arr_data = []
arr_data << scenario_block.map { |scenario| scenario.gold_start_game.mean }
arr_data << scenario_block.map { |scenario| scenario.gold_start_game.median }
#arr_labels = scenario_block.map { |scenario| scenario.name[0, 20] }
# store names
arr_labels = []
scenario_block.each do |scenario|
if scenario.name.length > 20
arr_labels << scenario.name[0, 20] + ".."
else
arr_labels << scenario.name
end
end
# create chart
obj_chart = chart_xy :data => arr_data,
:labels => arr_labels,
:size => [700, 200],
:colors => ['FF0000', '0000FF'],
:title => "Gold at start of the game: #{arr_labels.first} - #{arr_labels.last}",
:legend => ['Mean', 'Median'],
:legend_position => :top
# store chart
arr_charts << obj_chart
end
# return all charts for the given campaign
return arr_charts
end
# create chart for wins/losses/quits
def create_charts_wins_losses_quits
arr_charts = []
# for each block (batch) generate a chart
@scenario_blocks.each do |scenario_block|
arr_data = []
arr_data << scenario_block.map { |scenario| scenario.status_wins.percentage }
arr_data << scenario_block.map { |scenario| scenario.status_losses.percentage }
arr_data << scenario_block.map { |scenario| scenario.status_quits.percentage }
#arr_labels = scenario_block.map { |scenario| scenario.name[0, 20] }
# store names
arr_labels = []
scenario_block.each do |scenario|
if scenario.name.length > 20
arr_labels << scenario.name[0, 20] + ".."
else
arr_labels << scenario.name
end
end
# create chart
obj_chart = chart_bar :title => "Wins, Losses, Quits percentage: #{arr_labels.first} - #{arr_labels.last}",
:data => arr_data,
:size => [700 * scenario_block.size / 4, 250],
:grouped => true,
:width_spacing => [30, 5, 50],
:colors => ['006400', 'CC1100', 'EEAD0E'],
:legend => ['Wins %', 'Losses %', 'Quits %'],
:legend_position => :top,
:grid => [100, 10],
:axis_labels => [
{:axis => :y, :labels => Array.new(11) {|i| (i * 10).to_s}},
{:axis => :x, :labels => arr_labels}
]
# store chart
arr_charts << obj_chart
end
# return all charts for the given campaign
return arr_charts
end
# partition list of scenarios into blocks
# this helps to avoid data cluttering (create a series of charts)
private
def create_scenario_blocks
@scenario_blocks = []
arr_current_block = []
obj_trans = nil
pos = 0
@scenarios.each do |scenario|
if pos > 0 && 0 == pos % 3
arr_current_block << scenario
# store previous batch
@scenario_blocks << arr_current_block
# create new batch
arr_current_block = []
# store border element
if scenario != @scenarios.last
arr_current_block << scenario
end
else
# store scenario (current batch)
arr_current_block << scenario
end
pos += 1
end
# store last batch
unless arr_current_block.empty?
@scenario_blocks << arr_current_block
end
end
end
end

Some files were not shown because too many files have changed in this diff Show more