diff --git a/Makefile b/Makefile index 00738dc..3f21dfe 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,12 @@ BIN=cts all: main +staticgen: colors.o src/dbquery.c + echo "\nCompiling executable as static page generator\n" + gcc -c src/dbquery.c $(CFLAGS) -DSTATICGEN + gcc -c src/main.c $(CFLAGS) -DSTATICGEN + gcc colors.o dbquery.o main.o -lsqlite3 -o $(BIN) + main: main.o gcc colors.o dbquery.o main.o -lsqlite3 -o $(BIN) @@ -19,4 +25,4 @@ testcolor: src/colors.c src/tcolor.c gcc src/colors.c src/tcolor.c -o tcolor -I"includes" -g clean: - rm *.o *.log $(BIN) + rm *.o diff --git a/README.md b/README.md index 484212b..9ee6039 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ -C CGI Xonotic DeFrag ------------------ -A CGI program written in C to display data related to Race CTS leaderboards of Xonotic servers. +# xdfcgi + +A common gateway inferface (CGI) program written in C to display Race CTS leaderboards of Xonotic servers. It can also be a static page generator. ## Requirements -* SQLite3 + sqlite-devel python3 python-sqlite + +The first is only needed for compilation of the C program. The latter two are only for the auxiliary script `allmaps.py`. + +## Compiling +`make` makes a CGI program. + +`make staticgen` makes a static page generator. + +## Usage: CGI Query Strings +The program queries the database `db/cts.db` (`./src/dbquery.c`, function `static bool executequery`) -## Web Server Queries * `(none)` - Query file: `queries/mranks.sql` - - Requests the maplist of the server and related data. + - Requests the map list of the server and related data. * `?map=[map name]` - Query file: `queries/mleaderboard-ojoin.sql` @@ -18,11 +27,25 @@ A CGI program written in C to display data related to Race CTS leaderboards of X - Query file: `queries/rplayers.sql` - Requests a player's ranks for all maps leaderboards s/he is present on. +## Usage: Static Page Generation + + python scripts/allmaps.py + +The CGI program is still invoked in static generation. The files `allmaps.py`, `output/leaderboard.css`, `overview.html`, `map.html` produce the output. + +Before executing `allmaps.py`, copy and modify the templates. + + cp templates/overview.html . + cp templates/map.html . + +`allmaps.py` outputs an html file for all distinct maps in the database. The leaderboards for each map (equivalent to `?map=[map name]`) are in `output/maps/`. + ## Game Versions Used Under: * Xonotic 0.8.1 * Xonotic 0.8.2 ## Compilers +* gcc (GCC) 10.2.1 * MinGW, GCC 4.7.1 __________________ diff --git a/output/leaderboard.css b/output/leaderboard.css new file mode 100644 index 0000000..030da72 --- /dev/null +++ b/output/leaderboard.css @@ -0,0 +1,17 @@ +footer { + text-align:center; +} +table, th, td { + border: 1px solid grey; + border-collapse: collapse; +} +table { + width:100%; +} +th, td { + width:auto; + text-align:center; + word-wrap: break-word; + margin: 1em 1em 1em 1em; +} + diff --git a/queries/mranks.sql b/queries/mranks.sql index 1331a22..9d69e1f 100644 --- a/queries/mranks.sql +++ b/queries/mranks.sql @@ -5,4 +5,4 @@ where Cts_ranks.mapid = Cts_times.mapid and cryptokey = idvalue and idrank = 1 group by Cts_ranks.mapid -order by max(trank); \ No newline at end of file +order by count(trank) DESC; diff --git a/scripts/allmaps.py b/scripts/allmaps.py new file mode 100644 index 0000000..b184cd1 --- /dev/null +++ b/scripts/allmaps.py @@ -0,0 +1,67 @@ +import sqlite3 as sql +import subprocess, traceback + +# get all maps in database +def getmaps(database): + output = [] + con = sql.connect(database) + with con: + cursor = con.cursor() + try: + cursor.execute("select distinct mapid from Cts_times;") + output = cursor.fetchall() + except sql.Error: + print("Shit is fucked.") + return output + +# if there is no query then it outputs the index file. +def getcontent(query=None): + cmd = [("./cts")] + proc = subprocess.Popen(cmd, env=query, stdout=subprocess.PIPE, shell=True) + # communicate returns 'bytes' class with function 'decode' + return proc.communicate()[0].decode('utf-8') + +def renderindex(template): + # no env variable + table = getcontent() + filename = "./output/index.html" + with open(filename, 'w+') as fout: + fout.write(template % (table)) + fout.close + pass + +def main(): + template = "" + with open("overview.html", 'r') as fin: + template = fin.read() + renderindex(template) + maps = getmaps("db/cts.db") + with open("map.html", 'r') as fin: + template = fin.read() + # for each map generate an html file. + for game_map in maps: + # game_map is a tuple obj. + map_name = game_map[0] + query = {"QUERY_STRING" : ("map=%s" % map_name)} + table = getcontent(query) + filename = ("./output/maps/%s.html" % map_name) + with open(filename, 'w+') as fout: + title = map_name + fout.write(template.format( + title=title, + map_name=map_name, + table=table) + ) + # fout.write(template % (title, map_name, table)) + return True + +if __name__ == "__main__": + success = False + try: + success = main() + except FileNotFoundError: + traceback.print_exc() + print("\n\t The script probably didn't find the page templates needed to generate a page. You can copy minimal working examples from the repository at templates/.") + if success: + print("allmaps.py - Generated pages for all maps.") + pass diff --git a/src/dbquery.c b/src/dbquery.c index 7ae8dbb..7b331ec 100644 --- a/src/dbquery.c +++ b/src/dbquery.c @@ -31,7 +31,7 @@ static inline void print_tblheader(const char *c) { char *labels; switch (*c) { default: - labels = "\ + labels = "
\ \ \ \ @@ -41,7 +41,7 @@ static inline void print_tblheader(const char *c) { "; break; case QMLEADERBOARD: - labels = "


Map List

Name
\ + labels = "
\ \ \ \ @@ -50,7 +50,7 @@ static inline void print_tblheader(const char *c) { "; break; case QRPLAYER: - labels = "


Leaderboard

Rank
\ + labels = "
\ \ \ \ @@ -98,7 +98,11 @@ static void qresult(sqlite3_stmt * const sp, const char *c) { if (ISPLAYERNAME(c, i)) { print_plname(field); } else if (ISMAPNAME(c, i)) { +#ifdef STATICGEN + printf("", field, field); +#else printf("", field, field); +#endif } else if (i == 2 && (*c == QMLEADERBOARD || *c == QOVERVIEW)) { print_time(field); } else { diff --git a/src/main.c b/src/main.c index ea1b5a4..b2130ee 100644 --- a/src/main.c +++ b/src/main.c @@ -8,10 +8,7 @@ void html(void) { \ /v/ - Xonotic\ "; - const char *html_bot = "

Pages under construction.
\ - Service may sporadically become unavailable.
\ - In-game database is not directly synced with this web server.\ -

\ + const char *html_bot = "

\ "; const char *html_mid = "
\

hi / good luck and have fun.

Available Pages

\ @@ -29,7 +26,18 @@ void html(void) { printf("%s", html_bot); } +// use with template +void templated(void) { + const char *qstr = getenv("QUERY_STRING"); + getquery(qstr); + return; +} + int main(void) { +#ifdef STATICGEN + templated(); +#else html(); +#endif return 0; } diff --git a/templates/map.html b/templates/map.html new file mode 100644 index 0000000..5a77835 --- /dev/null +++ b/templates/map.html @@ -0,0 +1,22 @@ + + + + + + {title} - Leaderboards - Minimal Working Example + + + + + + +

{map_name}

+ + +{table} + + + + diff --git a/templates/overview.html b/templates/overview.html new file mode 100644 index 0000000..c4e0341 --- /dev/null +++ b/templates/overview.html @@ -0,0 +1,19 @@ + + + + + + CTS Leaderboards - Minimal Working Example + + + + + + +%s + + + +


Ranks

Name%s%s