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