1#!/usr/bin/python
2
3# Stormbaan Coureur highscore service
4# UDP based
5# (c)2006-2008 by Bram Stolk
6# Licensed under GPL
7
8import socket
9import string
10import signal
11import errno
12import sys
13
14
15signallist = [signal.SIGHUP,signal.SIGTERM,signal.SIGINT,signal.SIGQUIT]
16
17
18class Service :
19
20  def __init__(self) :
21
22    self.numscores = 92
23    self.filename = "./leaderboard.txt"
24
25    # We either read the table from file, or failing that
26    # we initialize the table anew.
27    if not self.read_table() :
28      self.init_table()
29
30    # Start listening for datagrams (not connections!)
31    self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
32    self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
33    self.sock.bind(("", 7460))
34
35
36  def __del__(self) :
37
38    self.sock.close()
39
40
41  def init_table(self) :
42
43    self.scores = [ ("noplayer", 999.99) for i in range(self.numscores) ]
44
45
46  def write_table(self) :
47
48    f = open(self.filename, "w")
49    assert f
50    f.write(self.table_string())
51    f.close()
52    print "scoreboard has been written to file"
53
54
55  def read_table(self) :
56
57    f = open(self.filename, "r")
58    if not f :
59      return False
60    lines = f.readlines()
61    if len(lines) != self.numscores :
62      return False
63    self.scores = [ (string.strip(line[:8]), float(line[9:])) for line in lines ]
64    print "scoreboard has been read from file"
65    return True
66
67
68  def table_string(self) :
69
70    retval=""
71    for s in self.scores :
72      n = "%-9s"  % (s[0],)
73      t = "%6.2f" % (s[1],)
74      retval = retval + n + t + "\n"
75    return retval
76
77
78  def get_player_idx(self, name) :
79
80    for i in range (self.numscores) :
81      if self.scores[i][0] == name :
82        return i
83    return -1
84
85
86  def handle_score(self, data) :
87
88    n = string.strip(data[:8]).replace(",",".")
89    t = float(data[9:15])
90    v = data[16:]
91    if t <= 0 :
92      return True
93
94    # Is it a personal best?
95    # if not, do not record it.
96    previdx = self.get_player_idx(n)
97    if previdx != -1 :
98      if self.scores[previdx][1] <= t :
99        return True
100      else :
101        # personal best: remove old one
102        self.scores.pop(previdx)
103
104    idx=0
105    while idx<len(self.scores) and self.scores[idx][1] < t :
106      idx+=1
107    if idx<self.numscores :
108      self.scores.insert(idx, (n,t))
109      if len(self.scores) > self.numscores :
110        self.scores.pop()
111    return True
112
113
114  def sustain(self) :
115
116    while True :
117      try :
118        (data,addr) = self.sock.recvfrom(1472)
119        print "got",data,"from", addr
120        ok = self.handle_score(data)
121        if ok :
122          self.sock.sendto(self.table_string(), 0, addr)
123      except socket.error, (e, msg) :
124        print e, msg
125        if e == errno.EAGAIN :
126          return
127        else :
128          self.alive = False
129
130
131
132def signal_hup_handler(signum, frame) :
133
134  global s
135  # reinstate old signalhandlers
136  for i in signallist :
137    signal.signal(i, signal.SIG_DFL)
138  s.write_table()
139  del s
140  sys.exit(0)
141
142
143if __name__ == '__main__' :
144
145  global s
146  s = Service()
147  for i in signallist :
148    signal.signal(i, signal_hup_handler)
149  s.sustain()
150
151
152