Added the demo branch.
[krufty_fps.git] / server_twisted.py
blob3751f163bb0d54736aaa55b09d48f9b8f607e7be
1 #!/usr/bin/python
3 # This is a twisted version of the server
5 import os, sys
6 from os.path import exists
8 import signal
9 from signal import *
11 from xml.dom.minidom import parse, parseString
13 from twisted.internet.protocol import DatagramProtocol
14 from twisted.internet import reactor
15 from twisted.internet import task
17 CONFIG = 'config.xml'
19 BROADCAST = 50030
20 MESSAGE = 40031
21 PASSWORD = "changethis"
22 NAME = "default"
24 ECHO_TIME = 1
26 ECHO_COUNT = 5 # this is the number of times one can ignore an echo until the server drops you
28 running = 1
30 from protocol import *
32 MULTICAST = '234.0.0.1'
34 def main():
36 # parsing command line options
38 if (len(sys.argv) > 1):
39 config_file = sys.argv[1]
40 else:
41 print "main(): No config file specified, using", CONFIG
42 config_file = CONFIG
44 global parsed_config
45 parsed_config = parse_config(config_file)
47 if (parsed_config['error']):
48 print "main(): Error parsing config, ending"
49 return
51 signal(SIGINT, handler)
53 run(parsed_config)
55 def run(configuration):
57 global running
59 if running:
60 print "run(): Beginning..."
62 global the_messenger
63 global the_broadcast
65 global members
66 members = {}
68 global ECHO_COUNT
70 if configuration.has_key('echo_number'):
71 ECHO_COUNT = configuration['echo_number']
72 else:
73 print "No echo_number specified in config file, using default of", ECHO_COUNT
75 the_broadcast = Broadcast()
76 the_messenger = Messenger()
78 the_messenger.echo_members = []
80 if configuration.has_key('broadcast'):
81 reactor.listenMulticast(configuration['broadcast'], the_broadcast)
82 else:
83 print "Server.__init__(): No broadcast socket number chosen, using", BROADCAST
84 reactor.listenMulticast(BROADCAST, the_broadcast)
86 if configuration.has_key('message'):
87 reactor.listenUDP(configuration['message'], the_messenger)
88 else:
89 print "Server.__init__(): No message socket number chosen, using", MESSAGE
90 reactor.listenUDP(MESSAGE, the_messenger)
92 echo_request = task.LoopingCall(the_messenger.echo_request)
94 if configuration.has_key('echo_time'):
95 echo_request.start(configuration['echo_time'])
96 else:
97 print "run(): No echo_time specified in config, using default of", ECHO_TIME
98 echo_request.start(ECHO_TIME)
100 reactor.run()
102 print "run(): Ending..."
104 return
106 def handler(signal, frame):
108 global running
109 running = 0
111 reactor.stop()
113 return
115 def is_end():
117 if not running:
118 reactor.stop()
120 return
122 def parse_config(filename):
124 parsed = {'error': 1}
126 if (exists(filename)):
127 dom = parse(filename)
128 else:
129 print "parse_config():", filename, "doesn't exist"
130 return parsed
132 if (dom.childNodes[0].nodeName == 'server_config'):
133 for node in dom.childNodes[0].childNodes:
135 if (node.nodeName == 'name' and len(node.childNodes)):
136 parsed['name'] = node.childNodes[0].nodeValue
138 if (node.nodeName == 'password' and len(node.childNodes)):
139 parsed['password'] = node.childNodes[0].nodeValue
141 if (node.nodeName == 'broadcast' and len(node.childNodes)):
142 parsed['broadcast'] = int(node.childNodes[0].nodeValue)
144 if (node.nodeName == 'message' and len(node.childNodes)):
145 parsed['message'] = int(node.childNodes[0].nodeValue)
147 if (node.nodeName == 'echo_time' and len(node.childNodes)):
148 parsed['echo_time'] = int(node.childNodes[0].nodeValue)
150 if (node.nodeName == 'echo_number' and len(node.childNodes)):
151 parsed['echo_number'] = int(node.childNodes[0].nodeValue)
153 parsed['error'] = 0
155 return parsed
157 class Broadcast(DatagramProtocol):
159 def startProtocol(self):
161 self.transport.joinGroup(MULTICAST)
163 def datagramReceived(self, data, address):
165 global parsed_config
167 if data[0:len(SERVERREQUEST)] == SERVERREQUEST:
169 new_message = ''.join([SERVEROFFER, " ",
170 str(parsed_config['message']),
171 " ", parsed_config['name']])
173 destination = (address[0], int(data[len(SERVERREQUEST) + 1:]))
175 the_messenger.transport.write(new_message, destination)
177 print "Broadcast.datagramReceived(): Received SERVERREQUEST, responded with SERVEROFFER"
179 return
181 class Messenger(DatagramProtocol):
183 def datagramReceived(self, data, (host, port)):
185 global members
186 global parsed_config
188 if data[:len(WANTIN)] == WANTIN:
190 if not members.has_key(data[len(WANTIN) + 1:]):
191 print "Messenger.datagramReceived(): WANTIN received, responding with YOUREIN"
193 response = ''.join([SOMEONEJOINED, " ", data[len(WANTIN) + 1:]])
195 for member in members:
196 self.transport.write(response, members[member])
198 members[data[len(WANTIN) + 1:]] = (host, port)
199 response = YOUREIN
200 self.transport.write(response, (host, port))
202 else:
203 if (host, port) in members.values():
204 print "Messenger.datagramReceived(): WANTIN received from a current member, responding with YOUREIN"
205 response = YOUREIN
206 self.transport.write(response, (host, port))
208 else:
209 response = NAMEDENIED
210 self.transport.write(response, (host, port))
212 if data[:len(IMHERE)] == IMHERE:
214 if (host, port) in members.values():
216 # print "Messenger.datagramReceived(): IMHERE received"
218 for name in members:
219 if members[name] == (host, port):
220 if name in self.echo_members:
221 while self.echo_members.count(name):
222 self.echo_members.remove(name)
224 if data[:len(YOUTHERE)] == YOUTHERE:
226 #print "Messenger.datagramReceived(): YOUTHERE received, responding with IMHERE"
228 self.transport.write(IMHERE, (host, port))
230 if data[:len(IMOUT)] == IMOUT:
232 if (host, port) in members.values():
233 print "Messenger.datagramReceived(): IMOUT received, notifying others"
235 response = ''.join([SOMEONELEFT, " "])
237 the_name = ''
238 for name in members.keys():
239 if members[name] == (host, port):
240 the_name = name
242 self.ejectMember(the_name)
244 else:
245 print "Messenger.datagramReceived(): IMOUT received, but sender not present in listing"
247 if data[:len(GETLIST)] == GETLIST:
248 print "Messenger.datagramReceived(): GETLIST received, responding with LIST"
250 response = ''.join([LIST, ' '])
252 for iname in members:
253 response = ''.join([response, iname, ' '])
255 self.transport.write(response, (host, port))
257 if data[:len(SERVERKILL)] == SERVERKILL:
259 if data[len(SERVERKILL) + 1:] == parsed_config['password']:
260 print "Messenger.datagramReceived(): SERVERKILL received, dying now."
261 reactor.stop()
263 if data[:len(LETTER)] == LETTER:
265 responses = []
267 origin = ''
269 for name in members:
270 if members[name] == (host, port):
271 origin = name
273 if origin == '':
274 print "Messenger.datagramReceived(): LETTER received, but originator not in listing"
275 else:
276 print "Messenger.datagramReceived(): LETTER received, forwarding it on..."
278 response = [''.join([LETTER, ':', origin, ':']), []]
279 message = ''
281 if data[len(LETTER):len(LETTER) + 2] == '::':
283 message = data[data.find("::") + 2:]
284 response[0] = ''.join([response[0], message])
286 for name in members:
287 if origin != name:
289 responses.append(response)
290 responses[-1][1] = members[name]
291 else:
293 message = data[the_data.find(':', data.find(':') + 1) + 1:]
294 response[0] = ''.join([response[0], messsage])
296 string_parts = data[data.find(':') + 1:
297 data.find(':', data.find(':') + 1)].split()
299 for dest in string_parts:
301 if members.has_key(dest):
303 responses.append(response)
304 responses[-1][1] = members[dest]
306 for response_i in responses:
308 self.transport.write(response_i[0], response_i[1])
310 return
312 def ejectMember(self, name):
314 reactor.callFromThread(self.ejectThreadMember, name)
316 return
318 def ejectThreadMember(self, name):
319 if name in members.keys():
320 response = YOUROUT
321 self.transport.write(response, members[name])
323 del members[name]
325 response = ''.join([SOMEONELEFT, " ", name])
327 for member in members:
329 self.transport.write(response, members[member])
330 print "notify debug", members[member], response
332 return
334 def echo_request(self):
336 global members
337 global parsed_config
338 global ECHO_COUNT
340 ejecters = []
342 if len(members):
343 #print "Messenger.echo_request(): Sending YOUTHERE messages..."
344 pass
346 for member in members:
348 if self.echo_members.count(member) > ECHO_COUNT:
349 ejecters.append(member)
351 else:
352 self.transport.write(STILLIN, members[member])
353 self.echo_members.append(member)
355 for person in ejecters:
357 self.ejectMember(person)
358 print "Messenger.echo_request(): Member ejected for suspicion of communism, name:", person
360 temp_members = []
362 for echo_request_item in self.echo_members:
363 if person != echo_request_item:
364 temp_members.append(echo_request_item)
366 self.echo_members = temp_members
368 return
370 if __name__ == '__main__':
371 main()