Removed the old unused stats code.
Fixes Debian bug #555276, CVE-2007-2383, CVE-2008-7720.
This commit is contained in:
parent
080cb58368
commit
48494a4b4b
170 changed files with 2 additions and 15049 deletions
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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 '&' ' '`
|
|
@ -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
|
|
@ -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&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 © 2003-2005 The Battle for Wesnoth</p>
|
||||
</div>
|
||||
|
||||
</div> <!-- end footer -->
|
||||
|
||||
</div> <!-- end main -->
|
||||
|
||||
</div> <!-- end global -->
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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=-&action=raw&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>
|
|
@ -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
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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&W_CAMPAIGN=%s&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;
|
||||
}
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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]
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
/* Returns NEXT game number. */
|
||||
|
||||
SELECT games_received FROM game_count;
|
|
@ -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]
|
|
@ -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]
|
|
@ -1 +0,0 @@
|
|||
2
|
|
@ -1,3 +0,0 @@
|
|||
/* Returns NEXT game number. */
|
||||
|
||||
SELECT games_received FROM game_count;
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -1 +0,0 @@
|
|||
3
|
|
@ -1,3 +0,0 @@
|
|||
/* Returns NEXT game number. */
|
||||
|
||||
SELECT games_received FROM game_count;
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -1,18 +0,0 @@
|
|||
1157260448000001491
|
||||
1157260448000001492
|
||||
1157260448000001493
|
||||
1157260448000001494
|
||||
1157260448000001495
|
||||
1157260448000001496
|
||||
1157260448000001497
|
||||
1157260448000001498
|
||||
1157260448000001499
|
||||
5876133823385251
|
||||
5876133823385252
|
||||
5876133823385253
|
||||
5876133823385254
|
||||
5876133823385255
|
||||
5876133823385256
|
||||
5876133823385257
|
||||
5876133823385258
|
||||
5876133823385259
|
|
@ -1,3 +0,0 @@
|
|||
/* Check all the serial numbers are in there */
|
||||
select * from serial order by id;
|
||||
select * from players order by id;
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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: You’re 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.
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,4 +0,0 @@
|
|||
# Methods added to this helper will be available to all templates in the application.
|
||||
module ApplicationHelper
|
||||
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
module CampaignHelper
|
||||
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
module VersionHelper
|
||||
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
class Player < ActiveRecord::Base
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
class ScenarioName < ActiveRecord::Base
|
||||
|
||||
# there are multiple games for each scenario
|
||||
has_many :games
|
||||
|
||||
end
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
class SpecialUnitName < ActiveRecord::Base
|
||||
|
||||
# there are multiple special units for each special_unit_name
|
||||
has_many :special_units
|
||||
end
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
class UnitName < ActiveRecord::Base
|
||||
|
||||
# there are multiple units for each unit_name
|
||||
has_many :units
|
||||
end
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => count_units_level_1 }
|
||||
%p
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => count_units_level_2 }
|
||||
%p
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => count_units_level_3 }
|
||||
%p
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => gold_start_game }
|
||||
%p
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => victory_percent_turns }
|
||||
%p
|
|
@ -1,2 +0,0 @@
|
|||
%img{ :src => wins_losses_quits }
|
||||
%p
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 }
|
|
@ -1,2 +0,0 @@
|
|||
.branch-section= render :partial => 'branch', :collection => @arr_branches
|
||||
|
|
@ -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!
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
Loading…
Add table
Reference in a new issue