Add master server web page CGI script to display a list of active

servers.

Subversion-branch: /master
Subversion-revision: 2199
This commit is contained in:
Simon Howard 2010-12-07 21:02:06 +00:00
parent 2d40ae549a
commit e83ce53a39
4 changed files with 231 additions and 0 deletions

BIN
web/cocoademon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

170
web/index.py Normal file
View file

@ -0,0 +1,170 @@
#!/usr/bin/env python
#
# Copyright(C) 2010 Simon Howard
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
#
# CGI script that generates master server web page.
#
from cgi import escape
from time import time
from select import select
import socket
import sys
import struct
import simplejson
NET_MASTER_PACKET_TYPE_GET_METADATA = 4
NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE = 5
METADATA_GATHER_TIME = 0.1 # seconds
MASTER_SERVER = "master.chocolate-doom.org"
UDP_PORT = 2342
def send_message(sock, addr, message_type, payload=None):
header = struct.pack(">h", message_type)
packet = header
if payload is not None:
packet += payload
sock.sendto(packet, addr)
def read_string(packet):
terminator = struct.pack("b", 0)
strlen = packet.index(terminator)
result, = struct.unpack("%ss" % strlen, packet[0:strlen])
return packet[strlen + 1:], result
def decode_string_list(packet):
""" Decode binary data containing NUL-terminated strings. """
strings = []
while len(packet) > 0:
packet, string = read_string(packet)
strings.append(string)
return strings
def process_metadata_response(packet):
""" Process a response received from the master server. """
type, = struct.unpack(">h", packet[0:2])
if type != NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE:
raise Exception("Wrong packet type received: %i" % type)
# Process the payload data (list of NUL-terminated strings)
strings = decode_string_list(packet[2:])
# Each string is a JSON-encoded dictionary. Decode these.
return map(simplejson.loads, strings)
def get_metadata(addr_str):
""" Query a master server for metadata about its servers. """
addr = (socket.gethostbyname(addr_str), UDP_PORT)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Send request to master.
send_message(sock, addr, NET_MASTER_PACKET_TYPE_GET_METADATA)
# Wait for METADATA_GATHER_TIME seconds to receive responses from
# the master. There may be multiple response packets if there are
# lots of servers.
servers = []
start_time = time()
while time() < start_time + METADATA_GATHER_TIME:
r, w, x = select([sock], [], [], METADATA_GATHER_TIME)
if sock in r:
packet, remote_addr = sock.recvfrom(1024)
servers += process_metadata_response(packet)
return servers
# CGI script to print out server list.
def get_server_data():
""" Query the master server and retrieve server metadata. """
return get_metadata(MASTER_SERVER)
def generate_table_row(server):
""" Generate a row of the HTML table, containing data for a
particular server. """
data = [
"%s:%i" % (server["address"], server["port"]),
escape(server["name"]),
escape(server["version"]),
server["max_players"]
]
result = []
for col in data:
result.append(" <td>%s</td>\n" % col)
return "<tr>\n" + ("".join(result)) + "</tr>\n"
def generate_table(server_data):
""" Generate an HTML table from list of server metadata. """
result = []
for server in server_data:
result.append(generate_table_row(server))
return "\n".join(result)
def read_template(filename):
""" Read HTML template file. """
file = open(filename)
result = file.read()
file.close()
return result
def output_html(html):
""" Output HTML data back to client. """
print "Content-Type: text/html"
print
print html
template = read_template("index.template")
server_data = get_server_data()
table_data = generate_table(server_data)
html = template.replace("___TABLE_DATA___", table_data)
output_html(html)

32
web/index.template Normal file
View file

@ -0,0 +1,32 @@
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css">
<title> Chocolate Doom master server </title>
</head>
<body>
<img id="cocoademon" src="cocoademon.png" alt="Cocoademon">
<h1> Chocolate Doom master server </h1>
<p> Currently active servers: </p>
<table>
<tr>
<th> Server address
<th> Server name
<th> Version
<th> Max. players
___TABLE_DATA___
</table>
<p>
For more information, see
<a href="http://www.chocolate-doom.org/">the website</a>.
</p>
</body>
</html>

29
web/style.css Normal file
View file

@ -0,0 +1,29 @@
body {
background-color: black;
color: white;
}
table {
border-color: white;
border-width: 0 0 1px 1px;
border-style: solid;
border-collapse: collapse;
}
td, th {
text-align: left;
border-color: white;
border-width: 1px 1px 0 0;
border-style: solid;
margin: 0;
padding: 4px;
}
#cocoademon {
float: right;
width: 150px;
border: 0px;
padding-right: 20px;
padding-top: 20px;
}