mirror of
https://github.com/chocolate-doom/master-server.git
synced 2024-11-14 00:41:09 +00:00
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:
parent
2d40ae549a
commit
e83ce53a39
4 changed files with 231 additions and 0 deletions
BIN
web/cocoademon.png
Normal file
BIN
web/cocoademon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
170
web/index.py
Normal file
170
web/index.py
Normal 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
32
web/index.template
Normal 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
29
web/style.css
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue