Mercurial > traipse_dev
comparison orpg/networking/mplay_server.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children | c7f04d3c76f5 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4385a7d0efd1 |
---|---|
1 #!/usr/bin/python2.1 | |
2 # Copyright (C) 2000-2001 The OpenRPG Project | |
3 # | |
4 # openrpg-dev@lists.sourceforge.net | |
5 # | |
6 # This program is free software; you can redistribute it and/or modify | |
7 # it under the terms of the GNU General Public License as published by | |
8 # the Free Software Foundation; either version 2 of the License, or | |
9 # (at your option) any later version. | |
10 # | |
11 # This program is distributed in the hope that it will be useful, | |
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 # GNU General Public License for more details. | |
15 # | |
16 # You should have received a copy of the GNU General Public License | |
17 # along with this program; if not, write to the Free Software | |
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 # -- | |
20 # | |
21 # File: mplay_server.py | |
22 # Author: Chris Davis | |
23 # Maintainer: | |
24 # Version: | |
25 # $Id: mplay_server.py,v 1.155 2008/01/24 03:52:03 digitalxero Exp $ | |
26 # | |
27 # Description: This file contains the code for the server of the multiplayer | |
28 # features in the orpg project. | |
29 # | |
30 | |
31 | |
32 # 04-15-2005 [Snowdog]: Added patch from Brandan Yares (xeriar). Reference: patch tracker id #1182076 | |
33 | |
34 __version__ = "$Id: mplay_server.py,v 1.155 2008/01/24 03:52:03 digitalxero Exp $" | |
35 | |
36 #!/usr/bin/env python | |
37 """ | |
38 <msg to='' from='' group_id='' /> | |
39 <player id='' ip='' group_id='' name='' action='new,del,group,update' status="" version=""/> | |
40 <group id='' name='' pwd='' players='' action='new,del,update' /> | |
41 <create_group from='' pwd='' name='' /> | |
42 <join_group from='' pwd='' group_id='' /> | |
43 <role action='set,get,display' player='' group_id='' boot_pwd='' role=''/> | |
44 """ | |
45 | |
46 from mplay_client import * | |
47 from mplay_client import MPLAY_LENSIZE | |
48 import orpg.dirpath | |
49 import orpg.tools.validate | |
50 import gc | |
51 import cgi | |
52 import sys | |
53 import string | |
54 import time | |
55 import urllib | |
56 from orpg.mapper.map_msg import * | |
57 from threading import Lock, RLock | |
58 from struct import pack, unpack, calcsize | |
59 from meta_server_lib import * | |
60 import traceback | |
61 import re | |
62 | |
63 # Import the minidom XML module | |
64 from xml.dom import minidom | |
65 | |
66 # Snag the version number | |
67 from orpg.orpg_version import * | |
68 | |
69 #Plugins | |
70 from server_plugins import ServerPlugins | |
71 | |
72 def id_compare(a,b): | |
73 "converts strings to intergers for list sort comparisons for group and player ids so they end up in numeric order" | |
74 return cmp(int(a),int(b)) | |
75 | |
76 | |
77 class game_group(object): | |
78 def __init__( self, id, name, pwd, desc="", boot_pwd="", minVersion="", mapFile=None, messageFile=None, persist =0 ): | |
79 self.id = id | |
80 self.name = name | |
81 self.desc = desc | |
82 self.minVersion = minVersion | |
83 self.messageFile = messageFile | |
84 self.players = [] | |
85 self.pwd = pwd | |
86 self.boot_pwd = boot_pwd | |
87 self.game_map = map_msg() | |
88 self.lock = Lock() | |
89 self.moderated = 0 | |
90 self.voice = {} | |
91 self.persistant = persist | |
92 self.mapFile = None | |
93 | |
94 if mapFile != None: | |
95 self.mapFile = mapFile | |
96 f = open( mapFile ) | |
97 tree = f.read() | |
98 f.close() | |
99 | |
100 else: | |
101 f = open(orpg.dirpath.dir_struct["template"] + "default_map.xml") | |
102 tree = f.read() | |
103 f.close() | |
104 | |
105 self.game_map.init_from_xml(tree) | |
106 | |
107 def save_map(self): | |
108 if self.mapFile is not None and self.persistant == 1 and self.mapFile.find("default_map.xml") == -1: | |
109 f = open(self.mapFile, "w") | |
110 f.write(self.game_map.get_all_xml()) | |
111 f.close() | |
112 | |
113 | |
114 def add_player(self,id): | |
115 self.players.append(id) | |
116 | |
117 def remove_player(self,id): | |
118 if self.voice.has_key(id): | |
119 del self.voice[id] | |
120 self.players.remove(id) | |
121 | |
122 def get_num_players(self): | |
123 num = len(self.players) | |
124 return num | |
125 | |
126 def get_player_ids(self): | |
127 tmp = self.players | |
128 return tmp | |
129 | |
130 | |
131 def check_pwd(self,pwd): | |
132 return (pwd==self.pwd) | |
133 | |
134 def check_boot_pwd(self,pwd): | |
135 return (pwd==self.boot_pwd) | |
136 | |
137 def check_version(self,ver): | |
138 if (self.minVersion == ""): | |
139 return 1 | |
140 minVersion=self.minVersion.split('.') | |
141 version=ver.split('.') | |
142 for i in xrange(min(len(minVersion),len(version))): | |
143 w=max(len(minVersion[i]),len(version[i])) | |
144 v1=minVersion[i].rjust(w); | |
145 v2=version[i].rjust(w); | |
146 if v1<v2: | |
147 return 1 | |
148 if v1>v2: | |
149 return 0 | |
150 | |
151 if len(minVersion)>len(version): | |
152 return 0 | |
153 return 1 | |
154 | |
155 #depreciated - see send_group_list() | |
156 def toxml(self,act="new"): | |
157 # Please don't add the boot_pwd to the xml, as this will give it away to players watching their console | |
158 xml_data = "<group id=\"" + self.id | |
159 xml_data += "\" name=\"" + self.name | |
160 xml_data += "\" pwd=\"" + str(self.pwd!="") | |
161 xml_data += "\" players=\"" + str(self.get_num_players()) | |
162 xml_data += "\" action=\"" + act + "\" />" | |
163 return xml_data | |
164 | |
165 | |
166 | |
167 class client_stub(client_base): | |
168 def __init__(self,inbox,sock,props,log): | |
169 client_base.__init__(self) | |
170 self.ip = props['ip'] | |
171 self.role = props['role'] | |
172 self.id = props['id'] | |
173 self.group_id = props['group_id'] | |
174 self.name = props['name'] | |
175 self.version = props['version'] | |
176 self.protocol_version = props['protocol_version'] | |
177 self.client_string = props['client_string'] | |
178 self.inbox = inbox | |
179 self.sock = sock | |
180 self.timeout_time = None | |
181 self.log_console = log | |
182 self.ignorelist = {} | |
183 | |
184 # implement from our base class | |
185 def isServer( self ): | |
186 return 1 | |
187 | |
188 def clear_timeout(self): | |
189 self.timeout_time = None | |
190 | |
191 def check_time_out(self): | |
192 if self.timeout_time==None: | |
193 self.timeout_time = time.time() | |
194 curtime = time.time() | |
195 diff = curtime - self.timeout_time | |
196 if diff > 1800: | |
197 return 1 | |
198 else: | |
199 return 0 | |
200 | |
201 def send(self,msg,player,group): | |
202 if self.get_status() == MPLAY_CONNECTED: | |
203 self.outbox.put("<msg to='" + player + "' from='0' group_id='" + group + "' />" + msg) | |
204 | |
205 def change_group(self,group_id,groups): | |
206 old_group_id = str(self.group_id) | |
207 groups[group_id].add_player(self.id) | |
208 groups[old_group_id].remove_player(self.id) | |
209 self.group_id = group_id | |
210 self.outbox.put(self.toxml('group')) | |
211 msg = groups[group_id].game_map.get_all_xml() | |
212 self.send(msg,self.id,group_id) | |
213 return old_group_id | |
214 | |
215 def self_message(self,act): | |
216 self.send(act,self.id,self.group_id) | |
217 | |
218 def take_dom(self,xml_dom): | |
219 self.name = xml_dom.getAttribute("name") | |
220 self.text_status = xml_dom.getAttribute("status") | |
221 | |
222 | |
223 ###################################################################### | |
224 ###################################################################### | |
225 ## | |
226 ## | |
227 ## MPLAY SERVER | |
228 ## | |
229 ## | |
230 ###################################################################### | |
231 ###################################################################### | |
232 | |
233 class mplay_server: | |
234 def __init__(self, log_console=None, name=None): | |
235 self.log_to_console = 1 | |
236 self.log_console = log_console | |
237 self.alive = 1 | |
238 self.players = {} | |
239 self.listen_event = Event() | |
240 self.incoming_event = Event() | |
241 self.incoming = Queue.Queue(0) | |
242 self.p_lock = RLock() | |
243 self.next_player_id = 1 | |
244 self.plugin_player_id = -1 | |
245 self.next_group_id = 100 | |
246 self.metas = {} # This holds the registerThread objects for each meta | |
247 self.be_registered = 0 # Status flag for whether we want to be registered. | |
248 self.serverName = name # Name of this server in the metas | |
249 self.boot_pwd = "" | |
250 self.server_address = None # IP or Name of server to post to the meta. None means the meta will auto-detect it. | |
251 self.defaultMessageFile = None | |
252 self.userPath = orpg.dirpath.dir_struct["user"] | |
253 self.lobbyMapFile = "Lobby_map.xml" | |
254 self.lobbyMessageFile = "LobbyMessage.html" | |
255 self.banFile = "ban_list.xml" | |
256 self.show_meta_messages = 0 | |
257 self.log_network_messages = 0 | |
258 self.allow_room_passwords = 1 | |
259 self.silent_auto_kick = 0 | |
260 self.zombie_time = 480 #time in minutes before a client is considered a ZOMBIE | |
261 self.minClientVersion = SERVER_MIN_CLIENT_VERSION | |
262 self.maxSendSize = 1024 | |
263 self.server_port = OPENRPG_PORT | |
264 self.allowRemoteKill = False | |
265 self.allowRemoteAdmin = True | |
266 self.sendLobbySound = False | |
267 self.lobbySound = 'http://www.digitalxero.net/music/mus_tavern1.bmu' | |
268 | |
269 def initServer(self, **kwargs): | |
270 for atter, value in kwargs.iteritems(): | |
271 setattr(self, atter, value) | |
272 self.validate = orpg.tools.validate.Validate(self.userPath) | |
273 self.validate.config_file( self.lobbyMapFile, "default_Lobby_map.xml" ) | |
274 self.validate.config_file( self.lobbyMessageFile, "default_LobbyMessage.html" ) | |
275 self.server_start_time = time.time() | |
276 | |
277 # Since the server is just starting here, we read in the XML configuration | |
278 # file. Notice the lobby is still created here by default. | |
279 self.groups = { '0': game_group('0','Lobby','','The game lobby', '', '', self.userPath + self.lobbyMapFile, self.userPath + self.lobbyMessageFile, 1)} | |
280 # Make sure the server's name gets set, in case we are being started from | |
281 # elsewhere. Basically, if it's passed in, we'll over ride what we were | |
282 # prompted for. This should never really happen at any rate. | |
283 | |
284 self.initServerConfig() | |
285 self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
286 self.listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
287 self.listen_thread = thread.start_new_thread(self.listenAcceptThread, (0,)) | |
288 self.in_thread = thread.start_new_thread(self.message_handler,(0,)) | |
289 | |
290 # Starts the player reaper thread. See self.player_reaper_thread_func() for more explanation | |
291 self.player_reaper_thread = thread.start_new_thread(self.player_reaper_thread_func,(0,)) | |
292 thread.start_new_thread(self.PluginThread,()) | |
293 self.svrcmds = {} | |
294 self.initsvrcmds() | |
295 self.ban_list = {} | |
296 self.initBanList() | |
297 | |
298 def addsvrcmd(self, cmd, function): | |
299 if not self.svrcmds.has_key(cmd): | |
300 self.svrcmds[cmd] = {} | |
301 self.svrcmds[cmd]['function'] = function | |
302 | |
303 def initsvrcmds(self): | |
304 self.addsvrcmd('msg', self.incoming_msg_handler) | |
305 self.addsvrcmd('player', self.incoming_player_handler) | |
306 self.addsvrcmd('admin', self.remote_admin_handler) | |
307 self.addsvrcmd('alter', self.do_alter) | |
308 self.addsvrcmd('role', self.do_role) | |
309 self.addsvrcmd('ping', self.do_ping) | |
310 self.addsvrcmd('system', self.do_system) | |
311 self.addsvrcmd('join_group', self.join_group) | |
312 self.addsvrcmd('create_group', self.create_group) | |
313 self.addsvrcmd('moderate', self.moderate_group) | |
314 self.addsvrcmd('plugin', self.plugin_msg_handler) | |
315 self.addsvrcmd('sound', self.sound_msg_handler) | |
316 | |
317 | |
318 # This method reads in the server's ban list added by Darren | |
319 def initBanList( self ): | |
320 self.log_msg("Processing Ban List File...") | |
321 | |
322 # make sure the server_ini.xml exists! | |
323 self.validate.config_file(self.banFile, "default_ban_list.xml" ) | |
324 | |
325 # try to use it. | |
326 try: | |
327 self.banDom = minidom.parse(self.userPath + 'ban_list.xml') | |
328 self.banDom.normalize() | |
329 self.banDoc = self.banDom.documentElement | |
330 | |
331 for element in self.banDom.getElementsByTagName('banned'): | |
332 playerName = element.getAttribute( 'name' ).replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">") | |
333 playerIP = element.getAttribute('ip') | |
334 self.ban_list[playerIP] = {} | |
335 self.ban_list[playerIP]['ip'] = playerIP | |
336 self.ban_list[playerIP]['name'] = playerName | |
337 self.log_msg(str(playerName) + " " + str(playerIP) + " is banned.") | |
338 self.saveBanList() | |
339 except Exception, e: | |
340 self.log_msg("Exception in initBanList() " + str(e)) | |
341 | |
342 # This method writes out the server's ban list added by Darren | |
343 def saveBanList( self ): | |
344 self.log_msg("Saving Ban List File...") | |
345 | |
346 # try to use it. | |
347 try: | |
348 data = [] | |
349 data.append("<server>\n") | |
350 for ip in self.ban_list: | |
351 data.append(' <banned name="' + str(self.ban_list[ip]['name'].replace("&", "&").replace("<", "<").replace(""", '"').replace(">", ">")) + '" ip="' + str(self.ban_list[ip]['ip']) + '" />' + "\n") | |
352 data.append("</server>") | |
353 file = open(self.userPath + self.banFile ,"w") | |
354 file.write("".join(data)) | |
355 file.close() | |
356 except Exception, e: | |
357 self.log_msg("Exception in saveBanList() " + str(e)) | |
358 | |
359 # This method reads in the server's configuration file and reconfigs the server | |
360 # as needed, over-riding any default values as requested. | |
361 def initServerConfig(self): | |
362 self.log_msg("Processing Server Configuration File... " + self.userPath) | |
363 # make sure the server_ini.xml exists! | |
364 self.validate.config_file( "server_ini.xml", "default_server_ini.xml" ) | |
365 # try to use it. | |
366 try: | |
367 self.configDom = minidom.parse(self.userPath + 'server_ini.xml') | |
368 self.configDom.normalize() | |
369 self.configDoc = self.configDom.documentElement | |
370 # Obtain the lobby/server password if it's been specified | |
371 if self.configDoc.hasAttribute("admin"): | |
372 self.boot_pwd = self.configDoc.getAttribute("admin") | |
373 elif self.configDoc.hasAttribute("boot"): | |
374 self.boot_pwd = self.configDoc.getAttribute("boot") | |
375 if hasattr(self, 'bootPassword'): | |
376 self.boot_pwd = self.bootPassword | |
377 elif len(self.boot_pwd) < 1: | |
378 self.boot_pwd = raw_input("Enter boot password for the Lobby: ") | |
379 if not hasattr(self, 'reg') and self.configDoc.hasAttribute("register"): | |
380 self.reg = self.configDoc.getAttribute("register") | |
381 if not len(self.reg) > 0 or self.reg[0].upper() not in ("Y", "N"): | |
382 opt = raw_input("Do you want to post your server to the OpenRPG Meta Server list? (y,n) ") | |
383 if len(opt) and (opt[0].upper() == 'Y'): | |
384 self.reg = 'Y' | |
385 else: | |
386 self.reg = 'N' | |
387 LobbyName = 'Lobby' | |
388 if self.configDoc.hasAttribute("lobbyname"): | |
389 LobbyName = self.configDoc.getAttribute("lobbyname") | |
390 map_node = service_node = self.configDoc.getElementsByTagName("map")[0] | |
391 msg_node = service_node = self.configDoc.getElementsByTagName("message")[0] | |
392 mapFile = map_node.getAttribute('file') | |
393 msgFile = msg_node.getAttribute('file') | |
394 if mapFile == '': | |
395 mapFile = 'Lobby_map.xml' | |
396 if msgFile == '': | |
397 msgFile = 'LobbyMessage.html' | |
398 # Update the lobby with the passwords if they've been specified | |
399 if len(self.boot_pwd): | |
400 self.groups = {'0': game_group( '0', LobbyName, "", 'The game lobby', self.boot_pwd, "", | |
401 self.userPath + mapFile.replace("myfiles/", ""), | |
402 self.userPath + msgFile.replace("myfiles/", ""), 1 ) | |
403 } | |
404 | |
405 # set ip or dns name to send to meta server | |
406 service_node = self.configDoc.getElementsByTagName("service")[0] | |
407 address = service_node.getAttribute("address") | |
408 address = address.lower() | |
409 if address == "" or address == "hostname/address" or address == "localhost": | |
410 self.server_address = None | |
411 else: | |
412 self.server_address = address | |
413 self.server_port = OPENRPG_PORT | |
414 if service_node.hasAttribute("port"): | |
415 self.server_port = int(service_node.getAttribute("port")) | |
416 if self.configDoc.hasAttribute("name") and len(self.configDoc.getAttribute("name")) > 0 : | |
417 self.name = self.configDoc.getAttribute("name") | |
418 else: | |
419 if self.reg[0].upper() == "Y": | |
420 if self.name == None: | |
421 self.name = raw_input("Server Name? ") | |
422 self.register() | |
423 | |
424 # Get the minimum openrpg version from config if available | |
425 # if it isn't set min version to internal default. | |
426 # | |
427 # server_ini.xml entry for version tag... | |
428 # <version min="x.x.x"> | |
429 try: | |
430 mver = self.configDoc.getElementsByTagName("version")[0] | |
431 self.minClientVersion = mver.getAttribute("min") | |
432 except: | |
433 self.minClientVersion = SERVER_MIN_CLIENT_VERSION #from orpg/orpg_version.py | |
434 self.defaultMessageFile = "" | |
435 # This try/except bit is to allow older versions of python to continue without a list error. | |
436 | |
437 | |
438 | |
439 #------------------------[ START <AUTOKICK> TAG PROCESSING ]-------------- | |
440 # Auto-kick option defaults for silent booting and | |
441 # setting the default zombie-client delay time --Snowdog 9/05 | |
442 # | |
443 # server_ini.xml entry for autikick tag... | |
444 # <autokick silent=["no","yes"] delay="(# of seconds)"> | |
445 | |
446 try: | |
447 ak = self.configDoc.getElementsByTagName("autokick")[0] | |
448 if ak.hasAttribute("silent"): | |
449 if ((ak.getAttribute("silent")).lower() == "yes"): | |
450 self.silent_auto_kick = 1 | |
451 else: | |
452 self.silent_auto_kick = 0 | |
453 if ak.hasAttribute("delay"): | |
454 try: | |
455 delay = int(ak.getAttribute("delay")) | |
456 self.zombie_time = delay | |
457 except: | |
458 #delay value cannot be converted into an int use defaut | |
459 self.zombie_time = 480 #(default 8 mins) | |
460 self.log_msg("**WARNING** Error with autokick delay string using default (480 sec)") | |
461 | |
462 except: | |
463 self.silent_auto_kick = 0 #(default to off) | |
464 self.zombie_time = 480 #(default 8 mins) | |
465 self.log_msg("**WARNING** Error loading autokick settings... using defaults") | |
466 | |
467 alk = "" | |
468 if (self.silent_auto_kick == 1): alk = "(Silent Mode)" | |
469 self.log_msg("Auto Kick: Delay="+str(self.zombie_time) + " " + alk) | |
470 #------------------------[ END <AUTOKICK> TAG PROCESSING ]-------------- | |
471 | |
472 | |
473 | |
474 #-------------------------------[ START <ROOM_DEFAULT> TAG PROCESSING ]-------------------- | |
475 # | |
476 # New room_defaults configuration option used to set various defaults | |
477 # for all user created rooms on the server. Incorporates akomans older | |
478 # default room message code (from above) --Snowdog 11/03 | |
479 # | |
480 # option syntax | |
481 # <room_defaults passwords="yes" map="myfiles/LobbyMap.xml" message="myfiles/LobbyMessage.html" /> | |
482 | |
483 #default settings for tag options... | |
484 roomdefault_msg = str(self.defaultMessageFile) #no message is the default | |
485 roomdefault_map = "" #use lobby map as default | |
486 roomdefault_pass = 1 #allow passwords | |
487 | |
488 | |
489 #pull information from config file DOM | |
490 try: | |
491 roomdefaults = self.configDom.getElementsByTagName("room_defaults")[0] | |
492 #rd.normalize() | |
493 #roomdefaults = self.rd.documentElement | |
494 try: | |
495 setting = roomdefaults.getElementsByTagName('passwords')[0] | |
496 rpw = setting.getAttribute('allow') | |
497 if rpw == "no" or rpw == "0": | |
498 roomdefault_pass = 0 | |
499 self.log_msg("Room Defaults: Disallowing Passworded Rooms") | |
500 else: | |
501 self.log_msg("Room Defaults: Allowing Passworded Rooms") | |
502 except: | |
503 self.log_msg("Room Defaults: [Warning] Allowing Passworded Rooms") | |
504 try: | |
505 setting = roomdefaults.getElementsByTagName('map')[0] | |
506 map = setting.getAttribute('file') | |
507 if map != "": | |
508 roomdefault_map = self.userPath + map.replace("myfiles/", "") | |
509 self.log_msg("Room Defaults: Using " + str(map) + " for room map") | |
510 except: | |
511 self.log_msg("Room Defaults: [Warning] Using Default Map") | |
512 | |
513 try: | |
514 setting = roomdefaults.getElementsByTagName('message')[0] | |
515 msg = setting.getAttribute('file') | |
516 if msg != "": | |
517 if msg[:4].lower() == 'http': | |
518 roomdefault_msg = msg | |
519 else: | |
520 roomdefault_msg = self.userPath + msg.replace("myfiles/", "") | |
521 self.log_msg("Room Defaults: Using " + str(msg) + " for room messages") | |
522 except: | |
523 print ("Room Defaults: [Warning] Using Default Message") | |
524 except: | |
525 traceback.print_exc() | |
526 self.log_msg("**WARNING** Error loading default room settings from configuration file. Using internal defaults.") | |
527 | |
528 | |
529 #set the defaults | |
530 if roomdefault_msg != "" or roomdefault_msg != None: | |
531 self.defaultMessageFile = roomdefault_msg #<room_defaults> tag superceeds older <newrooms> tag | |
532 else: | |
533 self.defaultMessageFile = None | |
534 | |
535 if roomdefault_map != "" or roomdefault_map != None: | |
536 self.defaultMapFile = roomdefault_map #<room_defaults> tag superceeds older <newrooms> tag | |
537 else: | |
538 self.defaultMapFile = None | |
539 | |
540 ##### room default map not handled yet. SETTING IGNORED | |
541 if roomdefault_pass == 0: self.allow_room_passwords = 0 | |
542 else: self.allow_room_passwords = 1 | |
543 | |
544 #-------------------------------[ END <ROOM_DEFAULT> TAG PROCESSING ]-------------------- | |
545 | |
546 | |
547 ###Server Cheat message | |
548 try: | |
549 cheat_node = self.configDoc.getElementsByTagName("cheat")[0] | |
550 self.cheat_msg = cheat_node.getAttribute("text") | |
551 except: | |
552 self.cheat_msg = "**FAKE ROLL**" | |
553 self.log_msg("**WARNING** <cheat txt=\"\"> tag missing from server configuration file. Using empty string.") | |
554 | |
555 | |
556 | |
557 # should validate protocal | |
558 validate_protocol_node = self.configDom.getElementsByTagName("validate_protocol ") | |
559 | |
560 self.validate_protocol = 1 | |
561 | |
562 if(validate_protocol_node): | |
563 self.validate_protocol = (validate_protocol_node[0].getAttribute("value") == "True") | |
564 if(self.validate_protocol != 1): | |
565 self.log_msg("Protocol Validation: OFF") | |
566 self.makePersistentRooms() | |
567 | |
568 self.log_msg("Server Configuration File: Processing Completed.") | |
569 | |
570 except Exception, e: | |
571 traceback.print_exc() | |
572 self.log_msg("Exception in initServerConfig() " + str(e)) | |
573 | |
574 | |
575 def makePersistentRooms(self): | |
576 'Creates rooms on the server as defined in the server config file.' | |
577 | |
578 for element in self.configDom.getElementsByTagName('room'): | |
579 roomName = element.getAttribute('name') | |
580 roomPassword = element.getAttribute('password') | |
581 bootPassword = element.getAttribute('boot') | |
582 | |
583 # Conditionally check for minVersion attribute | |
584 if element.hasAttribute('minVersion'): | |
585 minVersion = element.getAttribute('minVersion') | |
586 else: | |
587 minVersion = "" | |
588 | |
589 # Extract the map filename attribute from the map node | |
590 # we only care about the first map element found -- others are ignored | |
591 mapElement = element.getElementsByTagName('map')[0] | |
592 mapFile = self.userPath + mapElement.getAttribute('file').replace("myfiles/", "") | |
593 | |
594 messageElement = element.getElementsByTagName('message')[0] | |
595 messageFile = messageElement.getAttribute('file') | |
596 | |
597 if messageFile[:4] != 'http': | |
598 messageFile = self.userPath + messageFile.replace("myfiles/", "") | |
599 | |
600 # Make sure we have a message to even mess with | |
601 if(len(messageFile) == 0): | |
602 messageFile = self.defaultMessageFile | |
603 | |
604 if(len(mapFile) == 0): | |
605 mapFile = self.defaultMapFile | |
606 | |
607 moderated = 0 | |
608 if element.hasAttribute('moderated') and element.getAttribute('moderated').lower() == "true": | |
609 moderated = 1 | |
610 | |
611 #create the new persistant group | |
612 self.new_group(roomName, roomPassword, bootPassword, minVersion, mapFile, messageFile, persist = 1, moderated=moderated) | |
613 | |
614 | |
615 | |
616 def isPersistentRoom(self, id): | |
617 'Returns True if the id is a persistent room (other than the lobby), otherwise, False.' | |
618 | |
619 # altered persistance tracking from simple room id based to per-group setting | |
620 # allows arbitrary rooms to be marked as persistant without needing the self.persistRoomThreshold | |
621 # -- Snowdog 4/04 | |
622 try: | |
623 id = str(id) #just in case someone sends an int instead of a str into the function | |
624 if id not in self.groups: return 0 #invalid room, can't be persistant | |
625 pr = (self.groups[id]).persistant | |
626 return pr | |
627 except: | |
628 self.log_msg("Exception occured in isPersistentRoom(self,id)") | |
629 return 0 | |
630 | |
631 | |
632 | |
633 #----------------------------------------------------- | |
634 # Toggle Meta Logging -- Added by Snowdog 4/03 | |
635 #----------------------------------------------------- | |
636 def toggleMetaLogging(self): | |
637 if self.show_meta_messages != 0: | |
638 self.log_msg("Meta Server Logging: OFF") | |
639 self.show_meta_messages = 0 | |
640 else: | |
641 self.log_msg("Meta Server Logging: ON") | |
642 self.show_meta_messages = 1 | |
643 | |
644 | |
645 #----------------------------------------------------- | |
646 # Start/Stop Network Logging to File -- Added by Snowdog 4/03 | |
647 #----------------------------------------------------- | |
648 def NetworkLogging(self, mode = 0): | |
649 if mode == 0: | |
650 self.log_msg("Network Logging: OFF") | |
651 self.log_network_messages = 0 | |
652 elif mode == 1: | |
653 self.log_msg("Network Logging: ON (composite logfile)") | |
654 self.log_network_messages = 1 | |
655 elif mode == 2: | |
656 self.log_msg("Network Logging: ON (split logfiles)") | |
657 self.log_network_messages = 2 | |
658 else: return | |
659 #when log mode changes update all connection stubs | |
660 for n in self.players: | |
661 try: | |
662 self.players[n].EnableMessageLogging = mode | |
663 except: | |
664 self.log_msg("Error changing Message Logging Mode for client #" + str(self.players[n].id)) | |
665 def NetworkLoggingStatus(self): | |
666 if self.log_network_messages == 0: return "Network Traffic Log: Off" | |
667 elif self.log_network_messages == 1: return "Network Traffic Log: Logging (composite file)" | |
668 elif self.log_network_messages == 2: return "Network Traffic Log: Logging (inbound/outbound files)" | |
669 else: self.log_msg("Network Traffic Log: [Unknown]") | |
670 | |
671 | |
672 | |
673 | |
674 def register_callback(instance, xml_dom = None,source=None): | |
675 if xml_dom: # if we get something | |
676 if source == getMetaServerBaseURL(): # if the source of this DOM is the authoritative meta | |
677 try: | |
678 metacache_lock.acquire() | |
679 curlist = getRawMetaList() # read the raw meta cache lines into a list | |
680 updateMetaCache(xml_dom) # update the cache from the xml | |
681 newlist = getRawMetaList() # read it into a second list | |
682 finally: | |
683 metacache_lock.release() | |
684 | |
685 if newlist != curlist: # If the two lists aren't identical | |
686 # then something has changed. | |
687 instance.register() # Call self.register() | |
688 # which will force a re-read of the meta cache and | |
689 # redo the registerThreads | |
690 else: | |
691 instance.register() | |
692 | |
693 # Eventually, reset the MetaServerBaseURL here | |
694 | |
695 ## Added to help clean up parser errors in the XML on clients | |
696 ## due to characters that break welformedness of the XML from | |
697 ## the meta server. | |
698 ## NOTE: this is a stopgap measure -SD | |
699 def clean_published_servername(self, name): | |
700 #clean name of all apostrophes and quotes | |
701 badchars = "\"\\`><" | |
702 for c in badchars: | |
703 name = name.replace(c,"") | |
704 return name | |
705 | |
706 def registerRooms(self, args=None): | |
707 rooms = '' | |
708 id = '0' | |
709 time.sleep(500) | |
710 for rnum in self.groups.keys(): | |
711 rooms += urllib.urlencode( {"room_data[rooms][" + str(rnum) + "][name]":self.groups[rnum].name, | |
712 "room_data[rooms][" + str(rnum) + "][pwd]":str(self.groups[rnum].pwd != "")})+'&' | |
713 | |
714 for pid in self.groups[rnum].players: | |
715 rooms += urllib.urlencode( {"room_data[rooms][" + str(rnum) + "][players]["+str(pid)+"]":self.players[pid].name,})+'&' | |
716 | |
717 | |
718 for meta in self.metas.keys(): | |
719 while id == '0': | |
720 id, cookie = self.metas[meta].getIdAndCookie() | |
721 data = urllib.urlencode( {"room_data[server_id]":id, | |
722 "act":'registerrooms'}) | |
723 get_server_dom(data+'&'+rooms, self.metas[meta].path) | |
724 | |
725 | |
726 def register(self,name_given=None): | |
727 if name_given == None: | |
728 name = self.name | |
729 else: | |
730 self.name = name = name_given | |
731 | |
732 name = self.clean_published_servername(name) | |
733 | |
734 # Set up the value for num_users | |
735 if self.players: | |
736 num_players = len(self.players) | |
737 else: | |
738 num_players = 0 | |
739 | |
740 # request only Meta servers compatible with version 2 | |
741 metalist = getMetaServers(versions=["2"]) | |
742 if self.show_meta_messages != 0: | |
743 self.log_msg("Found these valid metas:") | |
744 for meta in metalist: | |
745 self.log_msg("Meta:" + meta) | |
746 | |
747 # Go through the list and see if there is already a running register | |
748 # thread for the meta. | |
749 # If so, call it's register() method | |
750 # If not, start one, implicitly calling the new thread's register() method | |
751 | |
752 | |
753 # iterate through the currently running metas and prune any | |
754 # not currently listed in the Meta Server list. | |
755 if self.show_meta_messages != 0: self.log_msg( "Checking running register threads for outdated metas.") | |
756 for meta in self.metas.keys(): | |
757 if self.show_meta_messages != 0: self.log_msg("meta:" + meta + ": ") | |
758 if not meta in metalist: # if the meta entry running is not in the list | |
759 if self.show_meta_messages != 0: self.log_msg( "Outdated. Unregistering and removing") | |
760 self.metas[meta].unregister() | |
761 del self.metas[meta] | |
762 else: | |
763 if self.show_meta_messages != 0: self.log_msg( "Found in current meta list. Leaving intact.") | |
764 | |
765 # Now call register() for alive metas or start one if we need one | |
766 for meta in metalist: | |
767 if self.metas.has_key(meta) and self.metas[meta] and self.metas[meta].isAlive(): | |
768 self.metas[meta].register(name=name, realHostName=self.server_address, num_users=num_players) | |
769 else: | |
770 self.metas[meta] = registerThread(name=name, realHostName=self.server_address, num_users=num_players, MetaPath=meta, port=self.server_port,register_callback=self.register_callback) | |
771 self.metas[meta].start() | |
772 | |
773 #The register Rooms thread | |
774 | |
775 self.be_registered = 1 | |
776 thread.start_new_thread(self.registerRooms,(0,)) | |
777 | |
778 | |
779 | |
780 def unregister(self): | |
781 # loop through all existing meta entries | |
782 # Don't rely on getMetaServers(), as a server may have been | |
783 # removed since it was started. In that case, then the meta | |
784 # would never get unregistered. | |
785 # | |
786 # Instead, loop through all existing meta threads and unregister them | |
787 | |
788 for meta in self.metas.values(): | |
789 if meta and meta.isAlive(): | |
790 meta.unregister() | |
791 | |
792 self.be_registered = 0 | |
793 | |
794 | |
795 | |
796 | |
797 # This method runs as it's own thread and does the group_member_check every | |
798 # sixty seconds. This should eliminate zombies that linger when no one is | |
799 # around to spook them. GC: Frequency has been reduced as I question how valid | |
800 # the implementation is as it will only catch a very small segment of lingering | |
801 # connections. | |
802 def player_reaper_thread_func(self,arg): | |
803 while self.alive: | |
804 time.sleep(60) | |
805 | |
806 self.p_lock.acquire() | |
807 for group in self.groups.keys(): | |
808 self.check_group_members(group) | |
809 self.p_lock.release() | |
810 | |
811 #This thread runs ever 250 miliseconds, and checks various plugin stuff | |
812 def PluginThread(self): | |
813 while self.alive: | |
814 self.p_lock.acquire() | |
815 players = ServerPlugins.getPlayer() | |
816 | |
817 for player in players: | |
818 if player is not None: | |
819 #Do something here so they can show up in the chat room for non web users' | |
820 pass | |
821 | |
822 data = ServerPlugins.preParseOutgoing() | |
823 | |
824 for msg in data: | |
825 try: | |
826 xml_dom = parseXml(msg) | |
827 xml_dom = xml_dom._get_documentElement() | |
828 | |
829 if xml_dom.hasAttribute('from') and int(xml_dom.getAttribute('from')) > -1: | |
830 xml_dom.setAttribute('from', '-1') | |
831 | |
832 xml_dom.setAttribute('to', 'all') | |
833 self.incoming_msg_handler(xml_dom, msg) | |
834 xml_dom.unlink() | |
835 except: | |
836 pass | |
837 | |
838 self.p_lock.release() | |
839 time.sleep(0.250) | |
840 | |
841 | |
842 def sendMsg( self, sock, msg, useCompression=False, cmpType=None): | |
843 """Very simple function that will properly encode and send a message to te | |
844 remote on the specified socket.""" | |
845 | |
846 if useCompression and cmpType != None: | |
847 mpacket = cmpType.compress(msg) | |
848 lpacket = pack('!i', len(mpacket)) | |
849 sock.send(lpacket) | |
850 | |
851 offset = 0 | |
852 while offset < len(mpacket): | |
853 slice = buffer(mpacket, offset, len(mpacket)-offset) | |
854 sent = sock.send(slice) | |
855 offset += sent | |
856 sentm = offset | |
857 else: | |
858 # Calculate our message length | |
859 length = len( msg ) | |
860 | |
861 # Encode the message length into network byte order | |
862 lp = pack('!i', length) | |
863 | |
864 try: | |
865 # Send the encoded length | |
866 sentl = sock.send( lp ) | |
867 | |
868 # Now, send the message the the length was describing | |
869 sentm = sock.send( msg ) | |
870 | |
871 except socket.error, e: | |
872 self.log_msg( e ) | |
873 | |
874 except Exception, e: | |
875 self.log_msg( e ) | |
876 | |
877 | |
878 def recvData( self, sock, readSize ): | |
879 """Simple socket receive method. This method will only return when the exact | |
880 byte count has been read from the connection, if remote terminates our | |
881 connection or we get some other socket exception.""" | |
882 | |
883 data = "" | |
884 offset = 0 | |
885 try: | |
886 while offset != readSize: | |
887 frag = sock.recv( readSize - offset ) | |
888 | |
889 # See if we've been disconnected | |
890 rs = len( frag ) | |
891 if rs <= 0: | |
892 # Loudly raise an exception because we've been disconnected! | |
893 raise IOError, "Remote closed the connection!" | |
894 | |
895 else: | |
896 # Continue to build complete message | |
897 offset += rs | |
898 data += frag | |
899 | |
900 except socket.error, e: | |
901 self.log_msg("Socket Error: recvData(): " + e ) | |
902 data = "" | |
903 | |
904 return data | |
905 | |
906 | |
907 | |
908 def recvMsg(self, sock, useCompression=False, cmpType=None): | |
909 """This method now expects to receive a message having a 4-byte prefix length. It will ONLY read | |
910 completed messages. In the event that the remote's connection is terminated, it will throw an | |
911 exception which should allow for the caller to more gracefully handles this exception event. | |
912 | |
913 Because we use strictly reading ONLY based on the length that is told to use, we no longer have to | |
914 worry about partially adjusting for fragmented buffers starting somewhere within a buffer that we've | |
915 read. Rather, it will get ONLY a whole message and nothing more. Everything else will remain buffered | |
916 with the OS until we attempt to read the next complete message.""" | |
917 | |
918 msgData = "" | |
919 try: | |
920 lenData = self.recvData( sock, MPLAY_LENSIZE ) | |
921 | |
922 # Now, convert to a usable form | |
923 (length,) = unpack('!i', lenData) | |
924 | |
925 # Read exactly the remaining amount of data | |
926 msgData = self.recvData( sock, length ) | |
927 | |
928 try: | |
929 if useCompression and cmpType != None: | |
930 msgData = cmpType.decompress(msgData) | |
931 except: | |
932 traceback.print_exc() | |
933 | |
934 except Exception, e: | |
935 self.log_msg( "Exception: recvMsg(): " + str(e) ) | |
936 | |
937 return msgData | |
938 | |
939 | |
940 | |
941 def kill_server(self): | |
942 self.alive = 0 | |
943 self.log_msg("Server stopping...") | |
944 self.unregister() # unregister from the Meta | |
945 for p in self.players.itervalues(): | |
946 p.disconnect() | |
947 self.incoming.put("<system/>") | |
948 | |
949 for g in self.groups.itervalues(): | |
950 g.save_map() | |
951 | |
952 try: | |
953 ip = socket.gethostbyname(socket.gethostname()) | |
954 kill = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
955 kill.connect((ip, self.server_port)) | |
956 | |
957 # Now, send the "system" command using the correct protocol format | |
958 self.sendMsg( kill, "<system/>" ) | |
959 kill.close() | |
960 except: | |
961 pass | |
962 | |
963 self.listen_sock.close() | |
964 self.listen_event.wait(10) | |
965 self.incoming_event.wait(10) | |
966 self.log_msg("Server stopped!") | |
967 | |
968 | |
969 | |
970 def log_msg(self,msg): | |
971 if self.log_to_console: | |
972 if self.log_console: | |
973 self.log_console(msg) | |
974 else: | |
975 print str(msg) | |
976 | |
977 | |
978 def print_help(self): | |
979 print | |
980 print "Commands: " | |
981 print "'kill' or 'quit' - to stop the server" | |
982 print "'broadcast' - broadcast a message to all players" | |
983 print "'list' - list players and groups" | |
984 print "'dump' - to dump player data" | |
985 print "'dump groups' - to list the group names and ids only" | |
986 print "'group n' - to list details about one group only" | |
987 print "'register' - To register the server as name. Also used to change the server's name if registered." | |
988 print "'unregister' - To remove this server from the list of servers" | |
989 print "'get lobby boot password' - to show the Lobby's boot password" | |
990 print "'set lobby boot password' - to set the Lobby's boot password" | |
991 print "'log' - toggles logging to the console off or on" | |
992 print "'log meta' - toggles logging of meta server messages on or off" | |
993 print "'logfile [off|on|split]' - timestamped network traffic log" | |
994 print "'remove room' - to remove a room from the server" | |
995 print "'kick' - kick a player from the server" | |
996 print "'ban' - ban a player from the server" | |
997 print "'remotekill' - This will toggle the ability to kill the server via the /admin command" | |
998 print "'monitor (#)' - monitors raw network I/O stream to specific client" | |
999 print "'purge clients' - boots all connected clients off the server immediately" | |
1000 print "'zombie [set [min]]' - view/set the auto-kick time for zombie clients" | |
1001 #drop any clients that are idle for more than 8 hours | |
1002 #as these are likely dead clientskick' - kick a player from the server" | |
1003 print "'uptime' - reports how long server has been running" | |
1004 print "'roompasswords' - allow/disallow room passwords (toggle)" | |
1005 print "'search' - will prompt for pattern and display results" | |
1006 print "'sendsize' - will ajust the send size limit" | |
1007 print "'remoteadmin' - will toggle remote admin commands" | |
1008 print "'togglelobbysound' - Will turn on or off the Auto sending of a sound to all players who join the loby" | |
1009 print "'lobbysound' - Lets you specify which sound file to send to players joining the lobby" | |
1010 print "'help' or '?' or 'h' - for this help message" | |
1011 print | |
1012 | |
1013 | |
1014 def broadcast(self,msg): | |
1015 self.send_to_all("0","<msg to='all' from='0' group_id='1'><font color='#FF0000'>" + msg + "</font>") | |
1016 | |
1017 | |
1018 def console_log(self): | |
1019 if self.log_to_console == 1: | |
1020 print "console logging now off" | |
1021 self.log_to_console = 0 | |
1022 else: | |
1023 print "console logging now on" | |
1024 self.log_to_console = 1 | |
1025 | |
1026 | |
1027 def groups_list(self): | |
1028 self.p_lock.acquire() | |
1029 try: | |
1030 keys = self.groups.keys() | |
1031 for k in keys: | |
1032 pw = "-" | |
1033 pr = " -" | |
1034 if self.groups[k].pwd != "": | |
1035 pw = "P" | |
1036 if self.isPersistentRoom( k ): | |
1037 pr = " S" #using S for static (P for persistant conflicts with password) | |
1038 print "Group: " + k + pr + pw + ' Name: ' + self.groups[k].name | |
1039 print | |
1040 | |
1041 except Exception, e: | |
1042 self.log_msg(str(e)) | |
1043 | |
1044 self.p_lock.release() | |
1045 | |
1046 #---------------------------------------------------------------- | |
1047 # Monitor Function -- Added by snowdog 2/05 | |
1048 #---------------------------------------------------------------- | |
1049 def monitor(self, pid, mode=1 ): | |
1050 "allows monitoring of a specific user(s) network i/o" | |
1051 #if mode is not set to 1 then monitor adds toggles the state | |
1052 #of monitoring on the given user | |
1053 | |
1054 if (mode == 1): | |
1055 for p in self.players: | |
1056 try: p.monitor("off") | |
1057 except: pass | |
1058 try: | |
1059 r = (self.players[pid]).set_traffic_monitor("toggle") | |
1060 self.log_msg("Monitor: Mode=" + str(r) + " on Player #" + str(pid)) | |
1061 except: | |
1062 self.log_msg("Monitor: Invalid Player ID") | |
1063 traceback.print_exc() | |
1064 | |
1065 | |
1066 def search(self,patern): | |
1067 keys = self.groups.keys() | |
1068 print "Search results:" | |
1069 for k in keys: | |
1070 ids = self.groups[k].get_player_ids() | |
1071 for id in ids: | |
1072 if self.players[id].id.find(patern)>-1: | |
1073 self.print_player_info(self.players[id]) | |
1074 | |
1075 elif self.players[id].name.find(patern)>-1: | |
1076 self.print_player_info(self.players[id]) | |
1077 | |
1078 elif self.players[id].ip.find(patern)>-1: | |
1079 self.print_player_info(self.players[id]) | |
1080 | |
1081 elif self.players[id].group_id.find(patern)>-1: | |
1082 self.print_player_info(self.players[id]) | |
1083 | |
1084 elif self.players[id].role.find(patern)>-1: | |
1085 self.print_player_info(self.players[id]) | |
1086 | |
1087 elif self.players[id].version.find(patern)>-1: | |
1088 self.print_player_info(self.players[id]) | |
1089 | |
1090 elif self.players[id].protocol_version.find(patern)>-1: | |
1091 self.print_player_info(self.players[id]) | |
1092 | |
1093 elif self.players[id].client_string.find(patern)>-1: | |
1094 self.print_player_info(self.players[id]) | |
1095 | |
1096 | |
1097 def print_player_info(self,player): | |
1098 print player.id,player.name,player.ip,player.group_id, player.role,player.version,player.protocol_version,player.client_string | |
1099 | |
1100 #---------------------------------------------------------------- | |
1101 # Uptime Function -- Added by snowdog 4/03 | |
1102 #---------------------------------------------------------------- | |
1103 def uptime(self , mode = 0): | |
1104 "returns string containing how long server has been in operation" | |
1105 ut = time.time() - self.server_start_time | |
1106 d = int(ut/86400) | |
1107 h = int( (ut-(86400*d))/3600 ) | |
1108 m = int( (ut-(86400*d)-(3600*h))/60) | |
1109 s = int( (ut-(86400*d)-(3600*h)-(60*m)) ) | |
1110 uts = str( "This server has been running for:\n " + str(d) + " days " + str(h) + " hours " + str(m) + " min. " + str(s) + " sec. [" + str(int(ut)) + " seconds]") | |
1111 if mode == 0: print uts | |
1112 else: return uts | |
1113 | |
1114 #----------------------------------------------------- | |
1115 # Toggle Room Password Allow -- Added by Snowdog 11/03 | |
1116 #----------------------------------------------------- | |
1117 def RoomPasswords(self): | |
1118 if self.allow_room_passwords != 0: | |
1119 self.allow_room_passwords = 0 | |
1120 return "Client Created Room Passwords: Disallowed" | |
1121 else: | |
1122 self.allow_room_passwords = 1 | |
1123 return "Client Created Room Passwords: Allowed" | |
1124 | |
1125 | |
1126 def group_dump(self,k): | |
1127 self.p_lock.acquire() | |
1128 try: | |
1129 print "Group: " + k | |
1130 print " Name: %s" % self.groups[k].name | |
1131 print " Desc: %s" % self.groups[k].desc | |
1132 print " Pass: %s" % self.groups[k].pwd | |
1133 print " Boot: %s" % self.groups[k].boot_pwd | |
1134 print " Moderated: %s" % self.groups[k].moderated | |
1135 print " Map: %s" % self.groups[k].game_map.get_all_xml() | |
1136 print | |
1137 except Exception, e: | |
1138 self.log_msg(str(e)) | |
1139 self.p_lock.release() | |
1140 | |
1141 #---------------------------------------------------------------- | |
1142 # Player List -- Added by snowdog 4/03 | |
1143 #---------------------------------------------------------------- | |
1144 def player_list(self): | |
1145 "display a condensed list of players on the server" | |
1146 self.p_lock.acquire() | |
1147 try: | |
1148 print "------------[ PLAYER LIST ]------------" | |
1149 keys = self.groups.keys() | |
1150 keys.sort(id_compare) | |
1151 for k in keys: | |
1152 groupstring = "Group " + str(k) + ": " + self.groups[k].name | |
1153 if self.groups[k].pwd != "": | |
1154 groupstring += " (Pass: \"" + self.groups[k].pwd + "\" )" | |
1155 print groupstring | |
1156 ids = self.groups[k].get_player_ids() | |
1157 ids.sort(id_compare) | |
1158 for id in ids: | |
1159 if self.players.has_key(id): | |
1160 print " (%s)%s [IP: %s] %s (%s)" % ((self.players[id]).id, (self.players[id]).name, (self.players[id]).ip, (self.players[id]).idle_status(), (self.players[id]).connected_time_string()) | |
1161 else: | |
1162 self.groups[k].remove_player(id) | |
1163 print "Bad Player Ref (#" + id + ") in group" | |
1164 if len(ids) > 0: print "" | |
1165 print "--------------------------------------" | |
1166 print "\nStatistics: groups: " + str(len(self.groups)) + " players: " + str(len(self.players)) | |
1167 except Exception, e: | |
1168 self.log_msg(str(e)) | |
1169 self.p_lock.release() | |
1170 | |
1171 | |
1172 def player_dump(self): | |
1173 self.p_lock.acquire() | |
1174 try: | |
1175 keys = self.groups.keys() | |
1176 for k in keys: | |
1177 print "Group: %s %s (pass: \"%s\")" % (str(k),self.groups[k].name, self.groups[k].pwd) | |
1178 | |
1179 ids = self.groups[k].get_player_ids() | |
1180 for id in ids: | |
1181 if self.players.has_key(id): | |
1182 print str(self.players[id]) | |
1183 else: | |
1184 self.groups[k].remove_player(id) | |
1185 print "Bad Player Ref (#" + id + ") in group" | |
1186 except Exception, e: | |
1187 self.log_msg(str(e)) | |
1188 | |
1189 self.p_lock.release() | |
1190 | |
1191 | |
1192 def update_request(self,newsock,xml_dom): | |
1193 # handle reconnects | |
1194 | |
1195 self.log_msg( "update_request() has been called." ) | |
1196 | |
1197 # get player id | |
1198 id = xml_dom.getAttribute("id") | |
1199 group_id = xml_dom.getAttribute("group_id") | |
1200 | |
1201 self.p_lock.acquire() | |
1202 if self.players.has_key(id): | |
1203 self.sendMsg( newsock, self.players[id].toxml("update"), self.players[id].useCompression, self.players[id].compressionType ) | |
1204 self.players[id].reset(newsock) | |
1205 self.players[id].clear_timeout() | |
1206 need_new = 0 | |
1207 else: | |
1208 need_new = 1 | |
1209 self.p_lock.release() | |
1210 | |
1211 if need_new: | |
1212 self.new_request(newsock,xml_dom) | |
1213 else: | |
1214 msg = self.groups[group_id].game_map.get_all_xml() | |
1215 self.send(msg,id,group_id) | |
1216 | |
1217 | |
1218 def new_request(self,newsock,xml_dom,LOBBY_ID='0'): | |
1219 #build client stub | |
1220 props = {} | |
1221 # Don't trust what the client tells us...trust what they connected as! | |
1222 props['ip'] = socket.gethostbyname( newsock.getpeername()[0] ) | |
1223 | |
1224 try: | |
1225 props['role'] = xml_dom.getAttribute("role") | |
1226 except: | |
1227 props['role'] = "GM" | |
1228 | |
1229 props['name'] = xml_dom.getAttribute("name") | |
1230 props['group_id'] = LOBBY_ID | |
1231 props['id'] = str(self.next_player_id) | |
1232 props['version'] = xml_dom.getAttribute("version") | |
1233 props['protocol_version'] = xml_dom.getAttribute("protocol_version") | |
1234 props['client_string'] = xml_dom.getAttribute("client_string") | |
1235 self.next_player_id += 1 | |
1236 new_stub = client_stub(self.incoming,newsock,props,self.log_console) | |
1237 if xml_dom.hasAttribute('useCompression'): | |
1238 new_stub.useCompression = True | |
1239 | |
1240 if xml_dom.hasAttribute('cmpType'): | |
1241 cmpType = xml_dom.getAttribute('cmpType') | |
1242 if cmpBZ2 and cmpType == 'bz2': | |
1243 new_stub.compressionType = bz2 | |
1244 elif cmpZLIB and cmpType == 'zlib': | |
1245 new_stub.compressionType = zlib | |
1246 else: | |
1247 new_stub.compressionType = None | |
1248 else: | |
1249 new_stub.compressionType = bz2 | |
1250 | |
1251 else: | |
1252 new_stub.useCompression = False | |
1253 | |
1254 #update newly create client stub with network logging state | |
1255 new_stub.EnableMessageLogging = self.log_network_messages | |
1256 | |
1257 self.sendMsg(newsock, new_stub.toxml("new"), False, None) | |
1258 | |
1259 # try to remove circular refs | |
1260 if xml_dom: | |
1261 xml_dom.unlink() | |
1262 | |
1263 # send confirmation | |
1264 data = self.recvMsg(newsock, new_stub.useCompression, new_stub.compressionType) | |
1265 try: | |
1266 xml_dom = parseXml(data) | |
1267 xml_dom = xml_dom._get_documentElement() | |
1268 except Exception, e: | |
1269 print e | |
1270 (remote_host,remote_port) = newsock.getpeername() | |
1271 bad_xml_string = "Your client sent an illegal message to the server and will be disconnected. " | |
1272 bad_xml_string += "Please report this bug to the development team at:<br /> " | |
1273 bad_xml_string += "<a href=\"http://sourceforge.net/tracker/?group_id=2237&atid=102237\">OpenRPG bugs " | |
1274 bad_xml_string += "(http://sourceforge.net/tracker/?group_id=2237&atid=102237)</a><br />" | |
1275 self.sendMsg( newsock, "<msg to='" + props['id'] + "' from='" + props['id'] + "' group_id='0' />" + bad_xml_string, new_stub.useCompression, new_stub.compressionType) | |
1276 | |
1277 time.sleep(2) | |
1278 newsock.close() | |
1279 print "Error in parse found from " + str(remote_host) + ". Disconnected." | |
1280 print " Offending data(" + str(len(data)) + "bytes)=" + data | |
1281 print "Exception=" + str(e) | |
1282 | |
1283 if xml_dom: | |
1284 xml_dom.unlink() | |
1285 return | |
1286 | |
1287 #start threads and store player | |
1288 | |
1289 allowed = 1 | |
1290 version_string = "" | |
1291 | |
1292 if ((props['protocol_version'] != PROTOCOL_VERSION) and self.validate_protocol): | |
1293 version_string = "Sorry, this server can't handle your client version. (Protocol mismatch)<br />" | |
1294 allowed = 0 | |
1295 | |
1296 if not self.checkClientVersion(props['version']): | |
1297 version_string = "Sorry, your client is out of date. <br />" | |
1298 version_string += "This server requires your client be version " + self.minClientVersion + " or higher to connect.<br />" | |
1299 allowed = 0 | |
1300 | |
1301 if not allowed: | |
1302 version_string += ' Please go to <a href="http://openrpg.digitalxero.net">http://openrpg.digitalxero.net</a> to find a compatible client.<br />' | |
1303 version_string += "If you can't find a compatible client on the website, chances are that the server is running an unreleased development version for testing purposes.<br />" | |
1304 | |
1305 self.sendMsg( newsock, "<msg to='" + props['id'] + "' from='0' group_id='0' />" + version_string, new_stub.useCompression, new_stub.compressionType) | |
1306 # Give messages time to flow | |
1307 time.sleep(1) | |
1308 self.log_msg("Connection terminating due to version incompatibility with client (ver: " + props['version'] + " protocol: " + props['protocol_version'] + ")" ) | |
1309 newsock.close() | |
1310 if xml_dom: | |
1311 xml_dom.unlink() | |
1312 return None | |
1313 | |
1314 ip = props['ip'] | |
1315 if self.ban_list.has_key(ip): | |
1316 banmsg = "You have been banned from this server.<br />" | |
1317 cmsg = "Banned Client: (" + str(props['id']) + ") " + str(props['name']) + " [" + str(props['ip']) + "]" | |
1318 self.log_msg(cmsg) | |
1319 allowed = 0 | |
1320 | |
1321 self.sendMsg( newsock, "<msg to='" + props['id'] + "' from='0' group_id='0' />" + banmsg, new_stub.useCompression, new_stub.compressionType) | |
1322 # Give messages time to flow | |
1323 time.sleep(1) | |
1324 newsock.close() | |
1325 if xml_dom: | |
1326 xml_dom.unlink() | |
1327 return None | |
1328 | |
1329 #---- Connection order changed by Snowdog 1/05 | |
1330 #---- Attempt to register player and send group data | |
1331 #---- before displaying lobby message | |
1332 #---- Does not solve the Blackhole bug but under some conditions may | |
1333 #---- allow for a graceful server response. -SD | |
1334 | |
1335 #---- changed method of sending group names to user 8/05 | |
1336 #---- black hole bug causes the group information to not be sent | |
1337 #---- to clients. Not sure why the group messages were being sent to the | |
1338 #---- incomming message queue, when they should be sent directly to user | |
1339 #---- Does not solve the black hole bug totally -SD | |
1340 | |
1341 try: | |
1342 if xml_dom.getAttribute("id") == props['id']: | |
1343 new_stub.initialize_threads() | |
1344 self.p_lock.acquire() | |
1345 self.players[props['id']] = new_stub | |
1346 self.groups[LOBBY_ID].add_player(props['id']) #always add to lobby on connection. | |
1347 self.send_group_list(props['id']) | |
1348 self.send_player_list(props['id'],LOBBY_ID) | |
1349 self.p_lock.release() | |
1350 | |
1351 msg = self.groups[LOBBY_ID].game_map.get_all_xml() | |
1352 self.send(msg,props['id'],LOBBY_ID) | |
1353 self.send_to_group(props['id'],LOBBY_ID,self.players[props['id']].toxml('new')) | |
1354 self.return_room_roles(props['id'],LOBBY_ID) | |
1355 | |
1356 # Re-initialize the role for this player incase they came from a different server | |
1357 self.handle_role("set",props['id'], "GM",self.groups[LOBBY_ID].boot_pwd, LOBBY_ID) | |
1358 | |
1359 cmsg = "Client Connect: (" + str(props['id']) + ") " + str(props['name']) + " [" + str(props['ip']) + "]" | |
1360 self.log_msg(cmsg) | |
1361 | |
1362 # If already registered then re-register, thereby updating the Meta | |
1363 # on the number of players | |
1364 if self.be_registered: | |
1365 self.register() | |
1366 except: | |
1367 traceback.print_exc() | |
1368 | |
1369 #something didn't go right. Notify client and drop the connection | |
1370 err_string = "<center>" | |
1371 err_string += "<hr><b>The server has encountered an error while processing your connection request.</b><hr>" | |
1372 err_string += "<br /><i>You are being disconnected from the server.</i><br />" | |
1373 err_string += "This error may represent a problem with the server. If you continue to get this message " | |
1374 err_string += "please contact the servers administrator to correct the issue.</center> " | |
1375 self.sendMsg( newsock, "<msg to='" + props['id'] + "' from='" + props['id'] + "' group_id='0' />" + err_string, new_stub.useCompression, new_stub.compressionType ) | |
1376 time.sleep(2) | |
1377 newsock.close() | |
1378 | |
1379 | |
1380 # Display the lobby message | |
1381 self.SendLobbyMessage(newsock,props['id']) | |
1382 | |
1383 if xml_dom: | |
1384 xml_dom.unlink() | |
1385 | |
1386 | |
1387 def checkClientVersion(self, clientversion): | |
1388 minv = self.minClientVersion.split('.') | |
1389 cver = clientversion.split('.') | |
1390 for i in xrange(min(len(minv),len(cver))): | |
1391 w=max(len(minv[i]),len(cver[i])) | |
1392 v1=minv[i].rjust(w); | |
1393 v2=cver[i].rjust(w); | |
1394 if v1<v2: | |
1395 return 1 | |
1396 if v1>v2: | |
1397 return 0 | |
1398 | |
1399 if len(minv)>len(cver): | |
1400 return 0 | |
1401 return 1 | |
1402 | |
1403 | |
1404 | |
1405 def SendLobbyMessage(self, socket, player_id): | |
1406 ####################################################################### | |
1407 # Display the lobby message | |
1408 # prepend this server's version string to the the lobby message | |
1409 try: | |
1410 lobbyMsg = "You have connected to an <a href=\"http://www.openrpg.com\">OpenRPG</a> server, version '" + VERSION + "'" | |
1411 | |
1412 # See if we have a server name to report! | |
1413 | |
1414 if len(self.serverName): | |
1415 lobbyMsg += ", named '" + self.serverName + "'." | |
1416 | |
1417 else: | |
1418 lobbyMsg += "." | |
1419 | |
1420 # Add extra line spacing | |
1421 lobbyMsg += "\n\n" | |
1422 | |
1423 try: | |
1424 self.validate.config_file("LobbyMessage.html","default_LobbyMessage.html") | |
1425 except: | |
1426 pass | |
1427 else: | |
1428 open_msg = open( self.userPath + "LobbyMessage.html", "r" ) | |
1429 lobbyMsg += open_msg.read() | |
1430 open_msg.close() | |
1431 | |
1432 # Send the server's lobby message to the client no matter what | |
1433 self.sendMsg(socket, "<msg to='" + player_id + "' from='0' group_id='0' />" + lobbyMsg, self.players[player_id].useCompression, self.players[player_id].compressionType) | |
1434 if self.sendLobbySound: | |
1435 self.sendMsg(socket, '<sound url="' + self.lobbySound + '" group_id="0" from="0" loop="True" />', self.players[player_id].useCompression, self.players[player_id].compressionType) | |
1436 return | |
1437 except: | |
1438 traceback.print_exc() | |
1439 # End of lobby message code | |
1440 ####################################################################### | |
1441 | |
1442 | |
1443 def listenAcceptThread(self,arg): | |
1444 # Set up the socket to listen on. | |
1445 try: | |
1446 self.log_msg("\nlisten thread running...") | |
1447 adder = "" | |
1448 if self.server_address is not None: | |
1449 adder = self.server_address | |
1450 self.listen_sock.bind(('', self.server_port)) | |
1451 self.listen_sock.listen(5) | |
1452 | |
1453 except Exception, e: | |
1454 self.log_msg(("Error binding request socket!", e)) | |
1455 self.alive = 0 | |
1456 | |
1457 | |
1458 while self.alive: | |
1459 | |
1460 # Block on the socket waiting for a new connection | |
1461 try: | |
1462 (newsock, addr) = self.listen_sock.accept() | |
1463 ## self.log_msg("New connection from " + str(addr)+ ". Interfacing with server...") | |
1464 | |
1465 # Now that we've accepted a new connection, we must immediately spawn a new | |
1466 # thread to handle it...otherwise we run the risk of having a DoS shoved into | |
1467 # our face! :O After words, this thread is dead ready for another connection | |
1468 # accept to come in. | |
1469 thread.start_new_thread(self.acceptedNewConnectionThread, ( newsock, addr )) | |
1470 | |
1471 except: | |
1472 print "The following exception caught accepting new connection:" | |
1473 traceback.print_exc() | |
1474 | |
1475 # At this point, we're done and cleaning up. | |
1476 self.log_msg("server socket listening thread exiting...") | |
1477 self.listen_event.set() | |
1478 | |
1479 | |
1480 | |
1481 def acceptedNewConnectionThread( self, newsock, addr ): | |
1482 """Once a new connection comes in and is accepted, this thread starts up to handle it.""" | |
1483 | |
1484 # Initialize xml_dom | |
1485 xml_dom = None | |
1486 data = None | |
1487 | |
1488 # get client info and send othe client info | |
1489 # If this receive fails, this thread should exit without even attempting to process it | |
1490 self.log_msg("Connection from " + str(addr) + " has been accepted. Waiting for data...") | |
1491 | |
1492 data = self.recvMsg( newsock ) | |
1493 | |
1494 if data=="" or data == None: | |
1495 self.log_msg("Connection from " + str(addr) + " failed. Closing connection.") | |
1496 try: | |
1497 newsock.close() | |
1498 except Exception, e: | |
1499 self.log_msg( str(e) ) | |
1500 print str(e) | |
1501 return #returning causes connection thread instance to terminate | |
1502 | |
1503 | |
1504 if data == "<system/>": | |
1505 try: | |
1506 newsock.close() | |
1507 except: | |
1508 pass | |
1509 return #returning causes connection thread instance to terminate | |
1510 | |
1511 # Clear out the xml_dom in preparation for new stuff, if necessary | |
1512 try: | |
1513 if xml_dom: | |
1514 xml_dom.unlink() | |
1515 | |
1516 except: | |
1517 self.log_msg( "The following exception caught unlinking xml_dom:") | |
1518 self.log_msg("Continuing") | |
1519 | |
1520 try: | |
1521 newsock.close() | |
1522 except: | |
1523 pass | |
1524 return #returning causes connection thread instance to terminate | |
1525 | |
1526 # Parse the XML received from the connecting client | |
1527 try: | |
1528 xml_dom = parseXml(data) | |
1529 xml_dom = xml_dom._get_documentElement() | |
1530 | |
1531 except: | |
1532 try: | |
1533 newsock.close() | |
1534 except: | |
1535 pass | |
1536 self.log_msg( "Error in parse found from " + str(addr) + ". Disconnected.") | |
1537 self.log_msg(" Offending data(" + str(len(data)) + "bytes)=" + data) | |
1538 self.log_msg( "Exception:") | |
1539 traceback.print_exc() | |
1540 return #returning causes connection thread instance to terminate | |
1541 | |
1542 # Determine the correct action and execute it | |
1543 try: | |
1544 # get action | |
1545 action = xml_dom.getAttribute("action") | |
1546 | |
1547 # Figure out what type of connection we have going on now | |
1548 if action == "new": | |
1549 self.new_request(newsock,xml_dom) | |
1550 | |
1551 elif action == "update": | |
1552 self.update_request(newsock,xml_dom) | |
1553 | |
1554 else: | |
1555 self.log_msg("Unknown Join Request!") | |
1556 | |
1557 except Exception, e: | |
1558 print "The following message: " + str(data) | |
1559 print "from " + str(addr) + " created the following exception: " | |
1560 traceback.print_exc() | |
1561 return #returning causes connection thread instance to terminate | |
1562 | |
1563 # Again attempt to clean out DOM stuff | |
1564 try: | |
1565 if xml_dom: | |
1566 xml_dom.unlink() | |
1567 except: | |
1568 print "The following exception caught unlinking xml_dom:" | |
1569 traceback.print_exc() | |
1570 return #returning causes connection thread instance to terminate | |
1571 | |
1572 | |
1573 | |
1574 #======================================================== | |
1575 # | |
1576 # Message_handler | |
1577 # | |
1578 #======================================================== | |
1579 # | |
1580 # Changed thread organization from one continuous parsing/handling thread | |
1581 # to multiple expiring parsing/handling threads to improve server performance | |
1582 # and player load capacity -- Snowdog 3/04 | |
1583 | |
1584 def message_handler(self,arg): | |
1585 xml_dom = None | |
1586 self.log_msg( "message handler thread running..." ) | |
1587 while self.alive: | |
1588 data = None | |
1589 try: | |
1590 data=self.incoming.get(0) | |
1591 except Queue.Empty: | |
1592 time.sleep(0.5) #sleep 1/2 second | |
1593 continue | |
1594 | |
1595 bytes = len(data) | |
1596 if bytes <= 0: | |
1597 continue | |
1598 try: | |
1599 thread.start_new_thread(self.parse_incoming_dom,(str(data),)) | |
1600 #data has been passed... unlink from the variable references | |
1601 #so data in passed objects doesn't change (python passes by reference) | |
1602 del data | |
1603 data = None | |
1604 except Exception, e: | |
1605 self.log_msg(str(e)) | |
1606 if xml_dom: xml_dom.unlink() | |
1607 if xml_dom: xml_dom.unlink() | |
1608 self.log_msg("message handler thread exiting...") | |
1609 self.incoming_event.set() | |
1610 | |
1611 def parse_incoming_dom(self,data): | |
1612 end = data.find(">") #locate end of first element of message | |
1613 head = data[:end+1] | |
1614 #self.log_msg(head) | |
1615 xml_dom = None | |
1616 try: | |
1617 xml_dom = parseXml(head) | |
1618 xml_dom = xml_dom._get_documentElement() | |
1619 self.message_action(xml_dom,data) | |
1620 | |
1621 except Exception, e: | |
1622 print "Error in parse of inbound message. Ignoring message." | |
1623 print " Offending data(" + str(len(data)) + "bytes)=" + data | |
1624 print "Exception=" + str(e) | |
1625 | |
1626 if xml_dom: xml_dom.unlink() | |
1627 | |
1628 | |
1629 def message_action(self, xml_dom, data): | |
1630 tag_name = xml_dom._get_tagName() | |
1631 if self.svrcmds.has_key(tag_name): | |
1632 self.svrcmds[tag_name]['function'](xml_dom,data) | |
1633 else: | |
1634 raise Exception, "Not a valid header!" | |
1635 #Message Action thread expires and closes here. | |
1636 return | |
1637 | |
1638 | |
1639 def do_alter(self, xml_dom, data): | |
1640 target = xml_dom.getAttribute("key") | |
1641 value = xml_dom.getAttribute("val") | |
1642 player = xml_dom.getAttribute("plr") | |
1643 group_id = xml_dom.getAttribute("gid") | |
1644 boot_pwd = xml_dom.getAttribute("bpw") | |
1645 actual_boot_pwd = self.groups[group_id].boot_pwd | |
1646 | |
1647 if self.allow_room_passwords == 0: | |
1648 msg ="<msg to='" + player + "' from='0' group_id='0' /> Room passwords have been disabled by the server administrator." | |
1649 self.players[player].outbox.put(msg) | |
1650 return | |
1651 elif boot_pwd == actual_boot_pwd: | |
1652 if target == "pwd": | |
1653 lmessage = "Room password changed to from \"" + self.groups[group_id].pwd + "\" to \"" + value + "\" by " + player | |
1654 self.groups[group_id].pwd = value | |
1655 msg ="<msg to='" + player + "' from='0' group_id='0' /> Room password changed to \"" + value + "\"." | |
1656 self.players[player].outbox.put(msg) | |
1657 self.log_msg(lmessage) | |
1658 self.send_to_all('0',self.groups[group_id].toxml('update')) | |
1659 elif target == "name": | |
1660 # Check for & in name. We want to allow this because of its common | |
1661 # use in d&d games | |
1662 result = self.change_group_name(group_id,value,player) | |
1663 msg ="<msg to='" + player + "' from='0' group_id='0' />" + result | |
1664 self.players[player].outbox.put(msg) | |
1665 else: | |
1666 msg ="<msg to='" + player + "' from='0' group_id='0'>Invalid Administrator Password." | |
1667 self.players[player].outbox.put(msg) | |
1668 | |
1669 | |
1670 def do_role(self, xml_dom, data): | |
1671 role = "" | |
1672 boot_pwd = "" | |
1673 act = xml_dom.getAttribute("action") | |
1674 player = xml_dom.getAttribute("player") | |
1675 group_id = xml_dom.getAttribute("group_id") | |
1676 if act == "set": | |
1677 role = xml_dom.getAttribute("role") | |
1678 boot_pwd = xml_dom.getAttribute("boot_pwd") | |
1679 xml_dom.unlink() | |
1680 if group_id != "0": | |
1681 self.handle_role(act, player, role, boot_pwd, group_id) | |
1682 self.log_msg(("role", (player, role))) | |
1683 | |
1684 def do_ping(self, xml_dom, data): | |
1685 player = xml_dom.getAttribute("player") | |
1686 group_id = xml_dom.getAttribute("group_id") | |
1687 sent_time = "" | |
1688 msg = "" | |
1689 try: | |
1690 sent_time = xml_dom.getAttribute("time") | |
1691 except: | |
1692 pass | |
1693 | |
1694 if sent_time != "": | |
1695 #because a time was sent return a ping response | |
1696 msg ="<ping time='" + str(sent_time) + "' />" | |
1697 else: | |
1698 msg ="<msg to='" + player + "' from='" + player + "' group_id='" + group_id + "'><font color='#FF0000'>PONG!?!</font>" | |
1699 | |
1700 self.players[player].outbox.put(msg) | |
1701 xml_dom.unlink() | |
1702 | |
1703 def do_system(self, xml_dom, data): | |
1704 pass | |
1705 | |
1706 def moderate_group(self,xml_dom,data): | |
1707 try: | |
1708 action = xml_dom.getAttribute("action") | |
1709 from_id = xml_dom.getAttribute("from") | |
1710 if xml_dom.hasAttribute("pwd"): | |
1711 pwd=xml_dom.getAttribute("pwd") | |
1712 else: | |
1713 pwd="" | |
1714 group_id=self.players[from_id].group_id | |
1715 | |
1716 if action == "list": | |
1717 if (self.groups[group_id].moderated): | |
1718 msg = "" | |
1719 for i in self.groups[group_id].voice.keys(): | |
1720 if msg != "": | |
1721 msg +=", " | |
1722 if self.players.has_key(i): | |
1723 msg += '('+i+') '+self.players[i].name | |
1724 else: | |
1725 del self.groups[group_id].voice[i] | |
1726 if (msg != ""): | |
1727 msg = "The following users may speak in this room: " + msg | |
1728 else: | |
1729 msg = "No people are currently in this room with the ability to chat" | |
1730 self.players[from_id].self_message(msg) | |
1731 else: | |
1732 self.players[from_id].self_message("This room is currently unmoderated") | |
1733 elif action == "enable": | |
1734 if not self.groups[group_id].check_boot_pwd(pwd): | |
1735 self.players[from_id].self_message("Failed - incorrect admin password") | |
1736 return | |
1737 self.groups[group_id].moderated = 1 | |
1738 self.players[from_id].self_message("This channel is now moderated") | |
1739 elif action == "disable": | |
1740 if not self.groups[group_id].check_boot_pwd(pwd): | |
1741 self.players[from_id].self_message("Failed - incorrect admin password") | |
1742 return | |
1743 self.groups[group_id].moderated = 0 | |
1744 self.players[from_id].self_message("This channel is now unmoderated") | |
1745 elif action == "addvoice": | |
1746 if not self.groups[group_id].check_boot_pwd(pwd): | |
1747 self.players[from_id].self_message("Failed - incorrect admin password") | |
1748 return | |
1749 users = xml_dom.getAttribute("users").split(',') | |
1750 for i in users: | |
1751 self.groups[group_id].voice[i.strip()]=1 | |
1752 elif action == "delvoice": | |
1753 if not self.groups[group_id].check_boot_pwd(pwd): | |
1754 self.players[from_id].self_message("Failed - incorrect admin password") | |
1755 return | |
1756 users = xml_dom.getAttribute("users").split(',') | |
1757 for i in users: | |
1758 if self.groups[group_id].voice.has_key(i.strip()): | |
1759 del self.groups[group_id].voice[i.strip()] | |
1760 else: | |
1761 print "Bad input: " + data | |
1762 | |
1763 except Exception,e: | |
1764 self.log_msg(str(e)) | |
1765 | |
1766 | |
1767 | |
1768 | |
1769 def join_group(self,xml_dom,data): | |
1770 try: | |
1771 from_id = xml_dom.getAttribute("from") | |
1772 pwd = xml_dom.getAttribute("pwd") | |
1773 group_id = xml_dom.getAttribute("group_id") | |
1774 ver = self.players[from_id].version | |
1775 allowed = 1 | |
1776 | |
1777 if not self.groups[group_id].check_version(ver): | |
1778 allowed = 0 | |
1779 msg = 'failed - invalid client version ('+self.groups[group_id].minVersion+' or later required)' | |
1780 | |
1781 if not self.groups[group_id].check_pwd(pwd): | |
1782 allowed = 0 | |
1783 | |
1784 #tell the clients password manager the password failed -- SD 8/03 | |
1785 pm = "<password signal=\"fail\" type=\"room\" id=\"" + group_id + "\" data=\"\"/>" | |
1786 self.players[from_id].outbox.put(pm) | |
1787 | |
1788 msg = 'failed - incorrect room password' | |
1789 | |
1790 if not allowed: | |
1791 self.players[from_id].self_message(msg) | |
1792 #the following line makes sure that their role is reset to normal, | |
1793 #since it is briefly set to lurker when they even TRY to change | |
1794 #rooms | |
1795 msg = "<role action=\"update\" id=\"" + from_id + "\" role=\"" + self.players[from_id].role + "\" />" | |
1796 self.players[from_id].outbox.put(msg) | |
1797 return | |
1798 | |
1799 #move the player into their new group. | |
1800 self.move_player(from_id, group_id) | |
1801 | |
1802 except Exception, e: | |
1803 self.log_msg(str(e)) | |
1804 | |
1805 | |
1806 | |
1807 | |
1808 #---------------------------------------------------------------------------- | |
1809 # move_player function -- added by Snowdog 4/03 | |
1810 # | |
1811 # Split join_group function in half. separating the player validation checks | |
1812 # from the actual group changing code. Done primarily to impliment | |
1813 # boot-from-room-to-lobby behavior in the server. | |
1814 | |
1815 def move_player(self, from_id, group_id ): | |
1816 "move a player from one group to another" | |
1817 try: | |
1818 try: | |
1819 if group_id == "0": | |
1820 self.players[from_id].role = "GM" | |
1821 else: | |
1822 self.players[from_id].role = "Lurker" | |
1823 except Exception, e: | |
1824 print "exception in move_player() " | |
1825 traceback.print_exc() | |
1826 | |
1827 old_group_id = self.players[from_id].change_group(group_id,self.groups) | |
1828 self.send_to_group(from_id,old_group_id,self.players[from_id].toxml('del')) | |
1829 self.send_to_group(from_id,group_id,self.players[from_id].toxml('new')) | |
1830 self.check_group(from_id, old_group_id) | |
1831 | |
1832 # Here, if we have a group specific lobby message to send, push it on | |
1833 # out the door! Make it put the message then announce the player...just | |
1834 # like in the lobby during a new connection. | |
1835 # -- only do this check if the room id is within range of known persistent id thresholds | |
1836 #also goes ahead if there is a defaultRoomMessage --akoman | |
1837 | |
1838 if self.isPersistentRoom(group_id) or self.defaultMessageFile != None: | |
1839 try: | |
1840 if self.groups[group_id].messageFile[:4] == 'http': | |
1841 data = urllib.urlretrieve(self.groups[group_id].messageFile) | |
1842 roomMsgFile = open(data[0]) | |
1843 else: | |
1844 roomMsgFile = open(self.groups[group_id].messageFile, "r") | |
1845 roomMsg = roomMsgFile.read() | |
1846 roomMsgFile.close() | |
1847 urllib.urlcleanup() | |
1848 | |
1849 except Exception, e: | |
1850 roomMsg = "" | |
1851 self.log_msg(str(e)) | |
1852 | |
1853 # Spit that darn message out now! | |
1854 self.players[from_id].outbox.put("<msg to='" + from_id + "' from='0' group_id='" + group_id + "' />" + roomMsg) | |
1855 | |
1856 if self.sendLobbySound and group_id == '0': | |
1857 self.players[from_id].outbox.put('<sound url="' + self.lobbySound + '" group_id="0" from="0" loop="True" />') | |
1858 | |
1859 # Now, tell everyone that we've arrived | |
1860 self.send_to_all('0', self.groups[group_id].toxml('update')) | |
1861 | |
1862 # this line sends a handle role message to change the players role | |
1863 self.send_player_list(from_id,group_id) | |
1864 | |
1865 #notify user about others in the room | |
1866 self.return_room_roles(from_id,group_id) | |
1867 self.log_msg(("join_group", (from_id, group_id))) | |
1868 self.handle_role("set", from_id, self.players[from_id].role, self.groups[group_id].boot_pwd, group_id) | |
1869 | |
1870 except Exception, e: | |
1871 self.log_msg(str(e)) | |
1872 | |
1873 thread.start_new_thread(self.registerRooms,(0,)) | |
1874 | |
1875 def return_room_roles(self,from_id,group_id): | |
1876 for m in self.players.keys(): | |
1877 if self.players[m].group_id == group_id: | |
1878 msg = "<role action=\"update\" id=\"" + self.players[m].id + "\" role=\"" + self.players[m].role + "\" />" | |
1879 self.players[from_id].outbox.put(msg) | |
1880 | |
1881 | |
1882 # This is pretty much the same thing as the create_group method, however, | |
1883 # it's much more generic whereas the create_group method is tied to a specific | |
1884 # xml message. Ack! This version simply creates the groups, it does not | |
1885 # send them to players. Also note, both these methods have race | |
1886 # conditions written all over them. Ack! Ack! | |
1887 def new_group( self, name, pwd, boot, minVersion, mapFile, messageFile, persist = 0, moderated=0 ): | |
1888 group_id = str( self.next_group_id ) | |
1889 self.next_group_id += 1 | |
1890 | |
1891 self.groups[group_id] = game_group( group_id, name, pwd, "", boot, minVersion, mapFile, messageFile, persist ) | |
1892 self.groups[group_id].moderated = moderated | |
1893 ins = "" | |
1894 if persist !=0: ins="Persistant " | |
1895 lmsg = "Creating " + ins + "Group... (" + str(group_id) + ") " + str(name) | |
1896 self.log_msg( lmsg ) | |
1897 | |
1898 | |
1899 def change_group_name(self,gid,name,pid): | |
1900 "Change the name of a group" | |
1901 # Check for & in name. We want to allow this because of its common | |
1902 # use in d&d games. | |
1903 try: | |
1904 loc = name.find("&") | |
1905 oldloc = 0 | |
1906 while loc > -1: | |
1907 loc = name.find("&",oldloc) | |
1908 if loc > -1: | |
1909 b = name[:loc] | |
1910 e = name[loc+1:] | |
1911 value = b + "&" + e | |
1912 oldloc = loc+1 | |
1913 | |
1914 loc = name.find("'") | |
1915 oldloc = 0 | |
1916 while loc > -1: | |
1917 loc = name.find("'",oldloc) | |
1918 if loc > -1: | |
1919 b = name[:loc] | |
1920 e = name[loc+1:] | |
1921 name = b + "'" + e | |
1922 oldloc = loc+1 | |
1923 | |
1924 loc = name.find('"') | |
1925 oldloc = 0 | |
1926 while loc > -1: | |
1927 loc = name.find('"',oldloc) | |
1928 if loc > -1: | |
1929 b = name[:loc] | |
1930 e = name[loc+1:] | |
1931 name = b + """ + e | |
1932 oldloc = loc+1 | |
1933 | |
1934 oldroomname = self.groups[gid].name | |
1935 self.groups[gid].name = str(name) | |
1936 lmessage = "Room name changed to from \"" + oldroomname + "\" to \"" + name + "\"" | |
1937 self.log_msg(lmessage + " by " + str(pid) ) | |
1938 self.send_to_all('0',self.groups[gid].toxml('update')) | |
1939 return lmessage | |
1940 except: | |
1941 return "An error occured during rename of room!" | |
1942 | |
1943 thread.start_new_thread(self.registerRooms,(0,)) | |
1944 | |
1945 | |
1946 | |
1947 def create_group(self,xml_dom,data): | |
1948 try: | |
1949 from_id = xml_dom.getAttribute("from") | |
1950 pwd = xml_dom.getAttribute("pwd") | |
1951 name = xml_dom.getAttribute("name") | |
1952 boot_pwd = xml_dom.getAttribute("boot_pwd") | |
1953 minVersion = xml_dom.getAttribute("min_version") | |
1954 #added var reassign -- akoman | |
1955 messageFile = self.defaultMessageFile | |
1956 | |
1957 # see if passwords are allowed on this server and null password if not | |
1958 if self.allow_room_passwords != 1: pwd = "" | |
1959 | |
1960 | |
1961 # | |
1962 # Check for & in name. We want to allow this because of its common | |
1963 # use in d&d games. | |
1964 | |
1965 loc = name.find("&") | |
1966 oldloc = 0 | |
1967 while loc > -1: | |
1968 loc = name.find("&",oldloc) | |
1969 if loc > -1: | |
1970 b = name[:loc] | |
1971 e = name[loc+1:] | |
1972 name = b + "&" + e | |
1973 oldloc = loc+1 | |
1974 | |
1975 loc = name.find("'") | |
1976 oldloc = 0 | |
1977 while loc > -1: | |
1978 loc = name.find("'",oldloc) | |
1979 if loc > -1: | |
1980 b = name[:loc] | |
1981 e = name[loc+1:] | |
1982 name = b + "'" + e | |
1983 oldloc = loc+1 | |
1984 | |
1985 loc = name.find('"') | |
1986 oldloc = 0 | |
1987 while loc > -1: | |
1988 loc = name.find('"',oldloc) | |
1989 if loc > -1: | |
1990 b = name[:loc] | |
1991 e = name[loc+1:] | |
1992 name = b + """ + e | |
1993 oldloc = loc+1 | |
1994 | |
1995 | |
1996 group_id = str(self.next_group_id) | |
1997 self.next_group_id += 1 | |
1998 self.groups[group_id] = game_group(group_id,name,pwd,"",boot_pwd, minVersion, None, messageFile ) | |
1999 self.groups[group_id].voice[from_id]=1 | |
2000 self.players[from_id].outbox.put(self.groups[group_id].toxml('new')) | |
2001 old_group_id = self.players[from_id].change_group(group_id,self.groups) | |
2002 self.send_to_group(from_id,old_group_id,self.players[from_id].toxml('del')) | |
2003 self.check_group(from_id, old_group_id) | |
2004 self.send_to_all(from_id,self.groups[group_id].toxml('new')) | |
2005 self.send_to_all('0',self.groups[group_id].toxml('update')) | |
2006 self.handle_role("set",from_id,"GM",boot_pwd, group_id) | |
2007 lmsg = "Creating Group... (" + str(group_id) + ") " + str(name) | |
2008 self.log_msg( lmsg ) | |
2009 jmsg = "moving to room " + str(group_id) + "." | |
2010 self.log_msg( jmsg ) | |
2011 #even creators of the room should see the HTML --akoman | |
2012 #edit: jan10/03 - was placed in the except statement. Silly me. | |
2013 if self.defaultMessageFile != None: | |
2014 if self.defaultMessageFile[:4] == 'http': | |
2015 data = urllib.urlretrieve(self.defaultMessageFile) | |
2016 open_msg = open(data[0]) | |
2017 urllib.urlcleanup() | |
2018 else: | |
2019 open_msg = open( self.defaultMessageFile, "r" ) | |
2020 | |
2021 roomMsg = open_msg.read() | |
2022 open_msg.close() | |
2023 # Send the rooms message to the client no matter what | |
2024 self.players[from_id].outbox.put( "<msg to='" + from_id + "' from='0' group_id='" + group_id + "' />" + roomMsg ) | |
2025 | |
2026 except Exception, e: | |
2027 self.log_msg( "Exception: create_group(): " + str(e)) | |
2028 | |
2029 thread.start_new_thread(self.registerRooms,(0,)) | |
2030 | |
2031 | |
2032 def check_group(self, from_id, group_id): | |
2033 try: | |
2034 if group_id not in self.groups: return | |
2035 if group_id == '0': | |
2036 self.send_to_all("0",self.groups[group_id].toxml('update')) | |
2037 return #never remove lobby *sanity check* | |
2038 if not self.isPersistentRoom(group_id) and self.groups[group_id].get_num_players() == 0: | |
2039 self.send_to_all("0",self.groups[group_id].toxml('del')) | |
2040 del self.groups[group_id] | |
2041 self.log_msg(("delete_group", (from_id, group_id))) | |
2042 | |
2043 else: | |
2044 self.send_to_all("0",self.groups[group_id].toxml('update')) | |
2045 | |
2046 #The register Rooms thread | |
2047 thread.start_new_thread(self.registerRooms,(0,)) | |
2048 | |
2049 except Exception, e: | |
2050 self.log_msg(str(e)) | |
2051 | |
2052 def del_player(self,id,group_id): | |
2053 try: | |
2054 dmsg = "Client Disconnect: (" + str(id) + ") " + str(self.players[id].name) | |
2055 self.players[id].disconnect() | |
2056 self.groups[group_id].remove_player(id) | |
2057 del self.players[id] | |
2058 self.log_msg(dmsg) | |
2059 | |
2060 | |
2061 # If already registered then re-register, thereby updating the Meta | |
2062 # on the number of players | |
2063 # Note: Upon server shutdown, the server is first unregistered, so | |
2064 # this code won't be repeated for each player being deleted. | |
2065 if self.be_registered: | |
2066 self.register() | |
2067 | |
2068 | |
2069 except Exception, e: | |
2070 self.log_msg(str(e)) | |
2071 | |
2072 self.log_msg("Explicit garbage collection shows %s undeletable items." % str(gc.collect())) | |
2073 | |
2074 | |
2075 | |
2076 def incoming_player_handler(self,xml_dom,data): | |
2077 id = xml_dom.getAttribute("id") | |
2078 act = xml_dom.getAttribute("action") | |
2079 #group_id = xml_dom.getAttribute("group_id") | |
2080 group_id = self.players[id].group_id | |
2081 ip = self.players[id].ip | |
2082 self.log_msg("Player with IP: " + str(ip) + " joined.") | |
2083 | |
2084 ServerPlugins.setPlayer(self.players[id]) | |
2085 | |
2086 self.send_to_group(id,group_id,data) | |
2087 if act=="new": | |
2088 try: | |
2089 self.send_player_list(id,group_id) | |
2090 self.send_group_list(id) | |
2091 except Exception, e: | |
2092 traceback.print_exc() | |
2093 elif act=="del": | |
2094 #print "del player" | |
2095 self.del_player(id,group_id) | |
2096 self.check_group(id, group_id) | |
2097 elif act=="update": | |
2098 self.players[id].take_dom(xml_dom) | |
2099 self.log_msg(("update", {"id": id, | |
2100 "name": xml_dom.getAttribute("name"), | |
2101 "status": xml_dom.getAttribute("status"), | |
2102 "role": xml_dom.getAttribute("role"), | |
2103 "ip": str(ip), | |
2104 "group": xml_dom.getAttribute("group_id"), | |
2105 "room": xml_dom.getAttribute("name"), | |
2106 "boot": xml_dom.getAttribute("rm_boot"), | |
2107 "version": xml_dom.getAttribute("version"), | |
2108 "ping": xml_dom.getAttribute("time") \ | |
2109 })) | |
2110 | |
2111 | |
2112 def strip_cheat_roll(self, string): | |
2113 try: | |
2114 cheat_regex = re.compile('&#91;(.*?)&#93;') | |
2115 string = cheat_regex.sub( r'[ ' + self.cheat_msg + " \\1 " + self.cheat_msg + ' ]', string) | |
2116 except: | |
2117 pass | |
2118 return string | |
2119 | |
2120 def strip_body_tags(self, string): | |
2121 try: | |
2122 bodytag_regex = re.compile('<\/?body(.*?)>') | |
2123 string = bodytag_regex.sub('', string) | |
2124 except: | |
2125 pass | |
2126 return string | |
2127 | |
2128 def msgTooLong(self, length): | |
2129 if length > self.maxSendSize and not self.maxSendSize == 0: | |
2130 return True | |
2131 return False | |
2132 | |
2133 def incoming_msg_handler(self,xml_dom,data): | |
2134 xml_dom, data = ServerPlugins.preParseIncoming(xml_dom, data) | |
2135 | |
2136 to_id = xml_dom.getAttribute("to") | |
2137 from_id = xml_dom.getAttribute("from") | |
2138 group_id = xml_dom.getAttribute("group_id") | |
2139 end = data.find(">") | |
2140 msg = data[end+1:] | |
2141 | |
2142 if from_id == "0" or len(from_id) == 0: | |
2143 print "WARNING!! Message received with an invalid from_id. Message dropped." | |
2144 return None | |
2145 | |
2146 # | |
2147 # check for < body to prevent someone from changing the background | |
2148 # | |
2149 | |
2150 data = self.strip_body_tags(data) | |
2151 | |
2152 # | |
2153 # check for [ and ] codes which are often used to cheat with dice. | |
2154 # | |
2155 if self.players[from_id].role != "GM": | |
2156 data = self.strip_cheat_roll(data) | |
2157 | |
2158 if group_id == '0' and self.msgTooLong(len(msg) and msg[:5] == '<chat'): | |
2159 self.send("Your message was too long, break it up into smaller parts please", from_id, group_id) | |
2160 self.log_msg('Message Blocked from Player: ' + self.players[from_id].name + ' attempting to send a message longer then ' + str(self.maxSendSize)) | |
2161 return | |
2162 | |
2163 if msg[:4] == '<map': | |
2164 if group_id == '0': | |
2165 #attempt to change lobby map. Illegal operation. | |
2166 self.players[from_id].self_message('The lobby map may not be altered.') | |
2167 elif to_id.lower() == 'all': | |
2168 #valid map for all players that is not the lobby. | |
2169 self.send_to_group(from_id,group_id,data) | |
2170 self.groups[group_id].game_map.init_from_xml(msg) | |
2171 else: | |
2172 #attempting to send map to specific individuals which is not supported. | |
2173 self.players[from_id].self_message('Invalid map message. Message not sent to others.') | |
2174 | |
2175 elif msg[:6] == '<boot ': | |
2176 self.handle_boot(from_id,to_id,group_id,msg) | |
2177 | |
2178 else: | |
2179 if to_id == 'all': | |
2180 if self.groups[group_id].moderated and not self.groups[group_id].voice.has_key(from_id): | |
2181 self.players[from_id].self_message('This room is moderated - message not sent to others') | |
2182 else: | |
2183 self.send_to_group(from_id,group_id,data) | |
2184 else: | |
2185 self.players[to_id].outbox.put(data) | |
2186 | |
2187 self.check_group_members(group_id) | |
2188 return | |
2189 | |
2190 def sound_msg_handler(self, xml_dom, data): | |
2191 from_id = xml_dom.getAttribute("from") | |
2192 group_id = xml_dom.getAttribute("group_id") | |
2193 if group_id != 0: | |
2194 self.send_to_group(from_id, group_id, data) | |
2195 | |
2196 def plugin_msg_handler(self,xml_dom,data): | |
2197 to_id = xml_dom.getAttribute("to") | |
2198 from_id = xml_dom.getAttribute("from") | |
2199 group_id = xml_dom.getAttribute("group_id") | |
2200 end = data.find(">") | |
2201 msg = data[end+1:] | |
2202 | |
2203 if from_id == "0" or len(from_id) == 0: | |
2204 print "WARNING!! Message received with an invalid from_id. Message dropped." | |
2205 return None | |
2206 | |
2207 | |
2208 if to_id == 'all': | |
2209 if self.groups[group_id].moderated and not self.groups[group_id].voice.has_key(from_id): | |
2210 self.players[from_id].self_message('This room is moderated - message not sent to others') | |
2211 else: | |
2212 self.send_to_group(from_id, group_id, msg) | |
2213 else: | |
2214 self.players[to_id].outbox.put(msg) | |
2215 | |
2216 self.check_group_members(group_id) | |
2217 return | |
2218 | |
2219 def handle_role(self, act, player, role, given_boot_pwd, group_id): | |
2220 if act == "display": | |
2221 msg = "<msg to=\"" + player + "\" from=\"0\" group_id=\"" + group_id + "\" />" | |
2222 msg += "Displaying Roles<br /><br /><u>Role</u>   <u>Player</u><br />" | |
2223 keys = self.players.keys() | |
2224 for m in keys: | |
2225 if self.players[m].group_id == group_id: | |
2226 msg += self.players[m].role + " " + self.players[m].name + "<br />" | |
2227 self.send(msg,player,group_id) | |
2228 elif act == "set": | |
2229 try: | |
2230 actual_boot_pwd = self.groups[group_id].boot_pwd | |
2231 if self.players[player].group_id == group_id: | |
2232 if actual_boot_pwd == given_boot_pwd: | |
2233 self.log_msg( "Administrator passwords match -- changing role") | |
2234 | |
2235 # Send update role event to all | |
2236 msg = "<role action=\"update\" id=\"" + player + "\" role=\"" + role + "\" />" | |
2237 self.send_to_group("0", group_id, msg) | |
2238 self.players[player].role = role | |
2239 if (role.lower() == "gm" or role.lower() == "player"): | |
2240 self.groups[group_id].voice[player]=1 | |
2241 else: | |
2242 #tell the clients password manager the password failed -- SD 8/03 | |
2243 pm = "<password signal=\"fail\" type=\"admin\" id=\"" + group_id + "\" data=\"\"/>" | |
2244 self.players[player].outbox.put(pm) | |
2245 self.log_msg( "Administrator passwords did not match") | |
2246 except Exception, e: | |
2247 print e | |
2248 print "Error executing the role change" | |
2249 print "due to the following exception:" | |
2250 traceback.print_exc() | |
2251 print "Ignoring boot message" | |
2252 | |
2253 def handle_boot(self,from_id,to_id,group_id,msg): | |
2254 xml_dom = None | |
2255 try: | |
2256 given_boot_pwd = None | |
2257 try: | |
2258 xml_dom = parseXml(msg) | |
2259 xml_dom = xml_dom._get_documentElement() | |
2260 given_boot_pwd = xml_dom.getAttribute("boot_pwd") | |
2261 | |
2262 except: | |
2263 print "Error in parse of boot message, Ignoring." | |
2264 print "Exception: " | |
2265 traceback.print_exc() | |
2266 | |
2267 try: | |
2268 actual_boot_pwd = self.groups[group_id].boot_pwd | |
2269 server_admin_pwd = self.groups["0"].boot_pwd | |
2270 | |
2271 self.log_msg("Actual boot pwd = " + actual_boot_pwd) | |
2272 self.log_msg("Given boot pwd = " + given_boot_pwd) | |
2273 | |
2274 if self.players[to_id].group_id == group_id: | |
2275 | |
2276 ### ---CHANGES BY SNOWDOG 4/03 --- | |
2277 ### added boot to lobby code. | |
2278 ### if boot comes from lobby dump player from the server | |
2279 ### any user in-room boot will dump to lobby instead | |
2280 if given_boot_pwd == server_admin_pwd: | |
2281 # Send a message to everyone in the room, letting them know someone has been booted | |
2282 boot_msg = "<msg to='all' from='%s' group_id='%s'/><font color='#FF0000'>Booting '(%s) %s' from server...</font>" % (from_id, group_id, to_id, self.players[to_id].name) | |
2283 | |
2284 self.log_msg("boot_msg:" + boot_msg) | |
2285 | |
2286 self.send_to_group( "0", group_id, boot_msg ) | |
2287 time.sleep( 1 ) | |
2288 | |
2289 self.log_msg("Booting player " + str(to_id) + " from server.") | |
2290 | |
2291 # Send delete player event to all | |
2292 self.send_to_group("0",group_id,self.players[to_id].toxml("del")) | |
2293 | |
2294 # Remove the player from local data structures | |
2295 self.del_player(to_id,group_id) | |
2296 | |
2297 # Refresh the group data | |
2298 self.check_group(to_id, group_id) | |
2299 | |
2300 elif actual_boot_pwd == given_boot_pwd: | |
2301 # Send a message to everyone in the room, letting them know someone has been booted | |
2302 boot_msg = "<msg to='all' from='%s' group_id='%s'/><font color='#FF0000'>Booting '(%s) %s' from room...</font>" % (from_id, group_id, to_id, self.players[to_id].name) | |
2303 | |
2304 self.log_msg("boot_msg:" + boot_msg) | |
2305 | |
2306 self.send_to_group( "0", group_id, boot_msg ) | |
2307 time.sleep( 1 ) | |
2308 | |
2309 #dump player into the lobby | |
2310 self.move_player(to_id,"0") | |
2311 | |
2312 # Refresh the group data | |
2313 self.check_group(to_id, group_id) | |
2314 else: | |
2315 #tell the clients password manager the password failed -- SD 8/03 | |
2316 pm = "<password signal=\"fail\" type=\"admin\" id=\"" + group_id + "\" data=\"\"/>" | |
2317 self.players[from_id].outbox.put(pm) | |
2318 print "boot passwords did not match" | |
2319 | |
2320 except Exception, e: | |
2321 traceback.print_exc() | |
2322 self.log_msg('Exception in handle_boot() ' + str(e)) | |
2323 | |
2324 finally: | |
2325 try: | |
2326 if xml_dom: | |
2327 xml_dom.unlink() | |
2328 except Exception, e: | |
2329 traceback.print_exc() | |
2330 self.log_msg('Exception in xml_dom.unlink() ' + str(e)) | |
2331 | |
2332 | |
2333 #--------------------------------------------------------------- | |
2334 # admin_kick function -- by Snowdog 4/03 | |
2335 # 9/17/05 updated to allow stealth boots (no client chat announce) -SD | |
2336 #--------------------------------------------------------------- | |
2337 def admin_kick(self, id, message="", silent = 0 ): | |
2338 "Kick a player from a server from the console" | |
2339 | |
2340 try: | |
2341 group_id = self.players[id].group_id | |
2342 # Send a message to everyone in the victim's room, letting them know someone has been booted | |
2343 boot_msg = "<msg to='all' from='0' group_id='%s'/><font color='#FF0000'>Kicking '(%s) %s' from server... %s</font>" % ( group_id, id, self.players[id].name, str(message)) | |
2344 self.log_msg("boot_msg:" + boot_msg) | |
2345 if (silent == 0): | |
2346 self.send_to_group( "0", group_id, boot_msg ) | |
2347 time.sleep( 1 ) | |
2348 | |
2349 self.log_msg("kicking player " + str(id) + " from server.") | |
2350 # Send delete player event to all | |
2351 self.send_to_group("0",group_id,self.players[id].toxml("del")) | |
2352 | |
2353 # Remove the player from local data structures | |
2354 self.del_player(id,group_id) | |
2355 | |
2356 # Refresh the group data | |
2357 self.check_group(id, group_id) | |
2358 | |
2359 except Exception, e: | |
2360 traceback.print_exc() | |
2361 self.log_msg('Exception in admin_kick() ' + str(e)) | |
2362 | |
2363 | |
2364 def admin_banip(self, ip, name="", silent = 0): | |
2365 "Ban a player from a server from the console" | |
2366 try: | |
2367 self.ban_list[ip] = {} | |
2368 self.ban_list[ip]['ip'] = ip | |
2369 self.ban_list[ip]['name'] = name | |
2370 self.saveBanList() | |
2371 | |
2372 except Exception, e: | |
2373 traceback.print_exc() | |
2374 self.log_msg('Exception in admin_banip() ' + str(e)) | |
2375 | |
2376 def admin_ban(self, id, message="", silent = 0): | |
2377 "Ban a player from a server from the console" | |
2378 try: | |
2379 id = str(id) | |
2380 group_id = self.players[id].group_id | |
2381 ip = self.players[id].ip | |
2382 self.ban_list[ip] = {} | |
2383 self.ban_list[ip]['ip'] = ip | |
2384 self.ban_list[ip]['name'] = self.players[id].name | |
2385 self.saveBanList() | |
2386 | |
2387 # Send a message to everyone in the victim's room, letting them know someone has been booted | |
2388 ban_msg = "<msg to='all' from='0' group_id='%s'/><font color='#FF0000'>Banning '(%s) %s' from server... %s</font>" % ( group_id, id, self.players[id].name, str(message)) | |
2389 self.log_msg("ban_msg:" + ban_msg) | |
2390 if (silent == 0): | |
2391 self.send_to_group("0", group_id, ban_msg) | |
2392 time.sleep( .1 ) | |
2393 | |
2394 self.log_msg("baning player " + str(id) + " from server.") | |
2395 # Send delete player event to all | |
2396 self.send_to_group("0", group_id, self.players[id].toxml("del")) | |
2397 | |
2398 # Remove the player from local data structures | |
2399 self.del_player(id, group_id) | |
2400 | |
2401 # Refresh the group data | |
2402 self.check_group(id, group_id) | |
2403 | |
2404 except Exception, e: | |
2405 traceback.print_exc() | |
2406 self.log_msg('Exception in admin_ban() ' + str(e)) | |
2407 | |
2408 def admin_unban(self, ip): | |
2409 try: | |
2410 if self.ban_list.has_key(ip): | |
2411 del self.ban_list[ip] | |
2412 | |
2413 self.saveBanList() | |
2414 | |
2415 except Exception, e: | |
2416 traceback.print_exc() | |
2417 self.log_msg('Exception in admin_unban() ' + str(e)) | |
2418 | |
2419 def admin_banlist(self): | |
2420 msg = [] | |
2421 msg.append('<table border="1"><tr><td><b>Name</b></td><td><b>IP</b></td></tr>') | |
2422 for ip in self.ban_list.keys(): | |
2423 msg.append("<tr><td>") | |
2424 msg.append(self.ban_list[ip]['name']) | |
2425 msg.append("</td><td>") | |
2426 msg.append(self.ban_list[ip]['ip']) | |
2427 msg.append("</td></tr>") | |
2428 msg.append("</table>") | |
2429 | |
2430 return "".join(msg) | |
2431 | |
2432 def admin_toggleSound(self): | |
2433 if self.sendLobbySound: | |
2434 self.sendLobbySound = False | |
2435 else: | |
2436 self.sendLobbySound = True | |
2437 | |
2438 return self.sendLobbySound | |
2439 | |
2440 def admin_soundFile(self, file): | |
2441 self.lobbySound = file | |
2442 | |
2443 def admin_setSendSize(self, sendlen): | |
2444 self.maxSendSize = sendlen | |
2445 self.log_msg('Max Send Size was set to ' + str(sendlen)) | |
2446 | |
2447 def remove_room(self, group): | |
2448 "removes a group and boots all occupants" | |
2449 #check that group id exists | |
2450 if group not in self.groups: | |
2451 return "Invalid Room Id. Ignoring remove request." | |
2452 | |
2453 self.groups[group].persistant = 0 | |
2454 try: | |
2455 keys = self.groups[group].get_player_ids() | |
2456 for k in keys: | |
2457 self.del_player(k, str(group)) | |
2458 self.check_group("0", str(group)) | |
2459 except: | |
2460 pass | |
2461 | |
2462 def send(self,msg,player,group): | |
2463 self.players[player].send(msg,player,group) | |
2464 | |
2465 | |
2466 def send_to_all(self,from_id,data): | |
2467 try: | |
2468 self.p_lock.acquire() | |
2469 keys = self.players.keys() | |
2470 self.p_lock.release() | |
2471 for k in keys: | |
2472 if k != from_id: | |
2473 self.players[k].outbox.put(data) | |
2474 except Exception, e: | |
2475 traceback.print_exc() | |
2476 self.log_msg("Exception: send_to_all(): " + str(e)) | |
2477 | |
2478 | |
2479 | |
2480 def send_to_group(self, from_id, group_id, data): | |
2481 data = ServerPlugins.postParseIncoming(data) | |
2482 try: | |
2483 keys = self.groups[group_id].get_player_ids() | |
2484 for k in keys: | |
2485 if k != from_id: | |
2486 self.players[k].outbox.put(data) | |
2487 except Exception, e: | |
2488 traceback.print_exc() | |
2489 self.log_msg("Exception: send_to_group(): " + str(e)) | |
2490 | |
2491 def send_player_list(self,to_id,group_id): | |
2492 try: | |
2493 keys = self.groups[group_id].get_player_ids() | |
2494 for k in keys: | |
2495 if k != to_id: | |
2496 data = self.players[k].toxml('new') | |
2497 self.players[to_id].outbox.put(data) | |
2498 except Exception, e: | |
2499 traceback.print_exc() | |
2500 self.log_msg("Exception: send_player_list(): " + str(e)) | |
2501 | |
2502 def send_group_list(self, to_id, action="new"): | |
2503 try: | |
2504 for key in self.groups: | |
2505 xml = self.groups[key].toxml(action) | |
2506 self.players[to_id].outbox.put(xml) | |
2507 except Exception, e: | |
2508 self.log_msg("Exception: send_group_list(): (client #"+to_id+") : " + str(e)) | |
2509 traceback.print_exc() | |
2510 | |
2511 #-------------------------------------------------------------------------- | |
2512 # KICK_ALL_CLIENTS() | |
2513 # | |
2514 # Convience method for booting all clients off the server at once. | |
2515 # used while troubleshooting mysterious "black hole" server bug | |
2516 # Added by Snowdog 11-19-04 | |
2517 def kick_all_clients(self): | |
2518 try: | |
2519 keys = self.groups.keys() | |
2520 for k in keys: | |
2521 pl = self.groups[k].get_player_ids() | |
2522 for p in pl: | |
2523 self.admin_kick(p,"Purged from server") | |
2524 except Exception, e: | |
2525 traceback.print_exc() | |
2526 self.log_msg("Exception: kick_all_clients(): " + str(e)) | |
2527 | |
2528 | |
2529 # This really has little value as it will only catch people that are hung | |
2530 # on a disconnect which didn't complete. Other idle connections which are | |
2531 # really dead go undeterred. | |
2532 # | |
2533 # UPDATED 11-29-04: Changed remove XML send to forced admin_kick for 'dead clients' | |
2534 # Dead clients now removed more effeciently as soon as they are detected | |
2535 # --Snowdog | |
2536 def check_group_members(self, group_id): | |
2537 try: | |
2538 keys = self.groups[group_id].get_player_ids() | |
2539 for k in keys: | |
2540 #drop any clients that are idle for more than 8 hours | |
2541 #as these are likely dead clients | |
2542 idlemins = self.players[k].idle_time() | |
2543 idlemins = idlemins/60 | |
2544 if (idlemins > self.zombie_time): | |
2545 self.admin_kick(k,"Removing zombie client", self.silent_auto_kick) | |
2546 elif self.players[k].get_status() != MPLAY_CONNECTED: | |
2547 if self.players[k].check_time_out(): | |
2548 self.log_msg("Player #" + k + " Lost connection!") | |
2549 self.admin_kick(k,"Removing dead client", self.silent_auto_kick) | |
2550 except Exception, e: | |
2551 self.log_msg("Exception: check_group_members(): " + str(e)) | |
2552 | |
2553 | |
2554 def remote_admin_handler(self,xml_dom,data): | |
2555 # handle incoming remove server admin messages | |
2556 # (allows basic administration of server from a remote client) | |
2557 # base message format: <admin id="" pwd="" cmd="" [data for command]> | |
2558 | |
2559 if not self.allowRemoteAdmin: | |
2560 return | |
2561 | |
2562 try: | |
2563 pid = xml_dom.getAttribute("id") | |
2564 gid = "" | |
2565 given_pwd = xml_dom.getAttribute("pwd") | |
2566 cmd = xml_dom.getAttribute("cmd") | |
2567 server_admin_pwd = self.groups["0"].boot_pwd | |
2568 p_id = "" | |
2569 p_name= "" | |
2570 p_ip = "" | |
2571 | |
2572 | |
2573 #verify that the message came from the proper ID/Socket and get IP address for logging | |
2574 if self.players.has_key(pid): | |
2575 p_name=(self.players[pid]).name | |
2576 p_ip=(self.players[pid]).ip | |
2577 gid=(self.players[pid]).group_id | |
2578 else: | |
2579 #invalid ID.. report fraud and log | |
2580 m = "Invalid Remote Server Control Message (invalid id) #" + str(pid) + " does not exist." | |
2581 self.log_msg( m ) | |
2582 return | |
2583 | |
2584 #log receipt of admin command added by Darren | |
2585 m = "Remote Server Control Message ( "+ str(cmd) +" ) from #" + str(pid) + " (" + str(p_name) + ") " + str(p_ip) | |
2586 self.log_msg ( m ) | |
2587 | |
2588 #check the admin password(boot password) against the supplied one in message | |
2589 #dump and log any attempts to control server remotely with invalid password | |
2590 if server_admin_pwd != given_pwd: | |
2591 #tell the clients password manager the password failed -- SD 8/03 | |
2592 pm = "<password signal=\"fail\" type=\"server\" id=\"" + str(self.players[pid].group_id) + "\" data=\"\"/>" | |
2593 self.players[pid].outbox.put(pm) | |
2594 m = "Invalid Remote Server Control Message (bad password) from #" + str(pid) + " (" + str(p_name) + ") " + str(p_ip) | |
2595 self.log_msg( m ) | |
2596 return | |
2597 | |
2598 #message now deemed 'authentic' | |
2599 #determine action to take based on command (cmd) | |
2600 | |
2601 if cmd == "list": | |
2602 #return player list to this user. | |
2603 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'>" + self.player_list_remote() | |
2604 self.players[pid].outbox.put(msg) | |
2605 | |
2606 elif cmd == "banip": | |
2607 ip = xml_dom.getAttribute("bip") | |
2608 name = xml_dom.getAttribute("bname") | |
2609 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'> Banned: " + str(ip) | |
2610 self.admin_banip(ip, name) | |
2611 | |
2612 elif cmd == "ban": | |
2613 id = xml_dom.getAttribute("bid") | |
2614 msg = "<msg to='" + id + "' from='0' group_id='" + gid + "'> Banned!" | |
2615 self.players[pid].outbox.put(msg) | |
2616 self.admin_ban(id, "") | |
2617 | |
2618 elif cmd == "unban": | |
2619 ip = xml_dom.getAttribute("ip") | |
2620 self.admin_unban(ip) | |
2621 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'> Unbaned: " + str(ip) | |
2622 self.players[pid].outbox.put(msg) | |
2623 | |
2624 elif cmd == "banlist": | |
2625 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>" + self.admin_banlist() | |
2626 self.players[pid].outbox.put(msg) | |
2627 | |
2628 elif cmd == "killgroup": | |
2629 ugid = xml_dom.getAttribute("gid") | |
2630 if ugid == "0": | |
2631 m = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>Cannot Remove Lobby! Remote administrator request denied!" | |
2632 self.players[pid].outbox.put(m) | |
2633 else: | |
2634 result = self.prune_room(ugid) | |
2635 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>" + str(result) | |
2636 self.players[pid].outbox.put(msg) | |
2637 | |
2638 elif cmd == "message": | |
2639 tuid = xml_dom.getAttribute("to_id") | |
2640 msg = xml_dom.getAttribute("msg") | |
2641 pmsg = "<msg to='" + tuid + "' from='0' group_id='" + self.players[tuid].group_id + "' >" + msg | |
2642 try: self.players[tuid].outbox.put(pmsg) | |
2643 except: | |
2644 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + ">Unknown Player ID: No message sent." | |
2645 self.players[pid].outbox.put(msg) | |
2646 | |
2647 elif cmd == "broadcast": | |
2648 bmsg = xml_dom.getAttribute("msg") | |
2649 self.broadcast(bmsg) | |
2650 | |
2651 elif cmd == "killserver" and self.allowRemoteKill: | |
2652 #dangerous command..once server stopped it must be restarted manually | |
2653 self.kill_server() | |
2654 | |
2655 elif cmd == "uptime": | |
2656 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'>" + self.uptime(1) | |
2657 self.players[pid].outbox.put(msg) | |
2658 | |
2659 elif cmd == "help": | |
2660 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>" | |
2661 msg += self.AdminHelpMessage() | |
2662 self.players[pid].outbox.put( msg) | |
2663 | |
2664 elif cmd == "roompasswords": | |
2665 # Toggle if room passwords are allowed on this server | |
2666 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>" | |
2667 msg += self.RoomPasswords() | |
2668 self.players[pid].outbox.put( msg) | |
2669 | |
2670 elif cmd == "createroom": | |
2671 rm_name = xml_dom.getAttribute("name") | |
2672 rm_pass = xml_dom.getAttribute("pass") | |
2673 rm_boot = xml_dom.getAttribute("boot") | |
2674 result = self.create_temporary_persistant_room(rm_name, rm_boot, rm_pass) | |
2675 msg = "<msg to='" + pid + "' from='0' group_id='" + gid + "'>" + result | |
2676 self.players[pid].outbox.put(msg) | |
2677 | |
2678 elif cmd == "nameroom": | |
2679 rm_id = xml_dom.getAttribute("rmid") | |
2680 rm_name = xml_dom.getAttribute("name") | |
2681 result = self.change_group_name(rm_id,rm_name,pid) | |
2682 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'/>" + result | |
2683 self.players[pid].outbox.put(msg) | |
2684 | |
2685 elif cmd == "passwd": | |
2686 tgid = xml_dom.getAttribute("gid") | |
2687 npwd = xml_dom.getAttribute("pass") | |
2688 if tgid == "0": | |
2689 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'>Server password may not be changed remotely!" | |
2690 self.players[pid].outbox.put(msg) | |
2691 else: | |
2692 try: | |
2693 self.groups[tgid].boot_pwd = npwd | |
2694 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'>Password changed for room " + tgid | |
2695 self.players[pid].outbox.put(msg) | |
2696 except: pass | |
2697 | |
2698 elif cmd == "savemaps": | |
2699 for g in self.groups.itervalues(): | |
2700 g.save_map() | |
2701 | |
2702 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'>Persistent room maps saved" | |
2703 self.players[pid].outbox.put(msg) | |
2704 | |
2705 | |
2706 else: | |
2707 msg ="<msg to='" + pid + "' from='0' group_id='" + gid + "'><i>[Unknown Remote Administration Command]</i>" | |
2708 self.players[pid].outbox.put(msg) | |
2709 | |
2710 | |
2711 except Exception, e: | |
2712 self.log_msg("Exception: Remote Admin Handler Error: " + str(e)) | |
2713 traceback.print_exc() | |
2714 | |
2715 | |
2716 def toggleRemoteKill(self): | |
2717 if self.allowRemoteKill: | |
2718 self.allowRemoteKill = False | |
2719 else: | |
2720 self.allowRemoteKill = True | |
2721 | |
2722 return self.allowRemoteKill | |
2723 | |
2724 def toggleRemoteAdmin(self): | |
2725 if self.allowRemoteAdmin: | |
2726 self.allowRemoteAdmin = False | |
2727 else: | |
2728 self.allowRemoteAdmin = True | |
2729 | |
2730 return self.allowRemoteAdmin | |
2731 | |
2732 #----------------------------------------------------------------- | |
2733 # Remote Administrator Help (returns from server not client) | |
2734 #----------------------------------------------------------------- | |
2735 def AdminHelpMessage(self): | |
2736 "returns a string to be sent as a message to a remote admin" | |
2737 | |
2738 #define the help command list information | |
2739 info = [] | |
2740 info.append( ['list', '/admin list', 'Displays information about rooms and players on the server'] ) | |
2741 info.append( ['uptime', '/admin uptime', 'Information on how long server has been running'] ) | |
2742 info.append( ['help', '/admin help', 'This help message']) | |
2743 info.append( ['passwd', '/admin passwd <group id> <new password>', 'Changes a rooms bootpassword. Server(lobby) password may not be changed']) | |
2744 info.append( ['roompasswords', '/admin roompasswords', 'Allow/Disallow Room Passwords on the server (toggles)']) | |
2745 info.append( ['message', '/admin message <user id> <message>', 'Send a message to a specific user on the server']) | |
2746 info.append( ['broadcast', '/admin broadcast <message>', 'Broadcast message to all players on server']) | |
2747 info.append( ['createroom', '/admin createroom <room name> <boot password> [password]', 'Creates a temporary persistant room if possible.<i>Rooms created this way are lost on server restarts']) | |
2748 info.append( ['nameroom', '/admin nameroom <group id> <new name>', 'Rename a room']) | |
2749 info.append( ['killgroup', '/admin killgroup <room id>', 'Remove a room from the server and kick everyone in it.']) | |
2750 if self.allowRemoteKill: | |
2751 info.append( ['killserver', '/admin killserver', 'Shuts down the server. <b>WARNING: Server cannot be restarted remotely via OpenRPG</b>']) | |
2752 info.append( ['ban', '/admin ban {playerId}', 'Ban a player from the server.']) | |
2753 info.append( ['unban', '/admin unban {bannedIP}', 'UnBan a player from the server.']) | |
2754 info.append( ['banlist', '/admin banlist', 'List Banned IPs and the Names associated with them']) | |
2755 info.append( ['savemaps', '/admin savemaps', 'Save all persistent room maps that are not using the default map file.']) | |
2756 | |
2757 | |
2758 #define the HTML for the help display | |
2759 FS = "<font size='-1'>" | |
2760 FE = "<font>" | |
2761 | |
2762 help = "<hr><B>REMOTE ADMINISTRATOR COMMANDS SUPPORTED</b><br /><br />" | |
2763 help += "<table border='1' cellpadding='2'>" | |
2764 help += "<tr><td width='15%'><b>Command</b></td><td width='25%'><b>Format</b></td><td width='60%'><b>Description</b></td></tr>" | |
2765 for n in info: | |
2766 help += "<tr><td>" + FS + n[0] + FE + "</td><td><nobr>" + FS + n[1] + FE + "</nobr></td><td>" + FS + n[2] + FE + "</td></tr>" | |
2767 help += "</table>" | |
2768 return help | |
2769 | |
2770 | |
2771 #---------------------------------------------------------------- | |
2772 # Create Persistant Group -- Added by Snowdog 6/03 | |
2773 # | |
2774 # Allows persistant groups to be created on the fly. | |
2775 # These persistant groups are not added to the server.ini file | |
2776 # however and are lost on server restarts | |
2777 # | |
2778 # Updated function code to use per-group based persistance and | |
2779 # removed references to outdated persistRoomIdThreshold | |
2780 #---------------------------------------------------------------- | |
2781 | |
2782 def create_temporary_persistant_room(self, roomname, bootpass, password=""): | |
2783 # if the room id just above the persistant room limit is available (not in use) | |
2784 # then it will be assigned as a persistant room on the server | |
2785 "create a temporary persistant room" | |
2786 | |
2787 group_id = str(self.next_group_id) | |
2788 self.next_group_id += 1 | |
2789 self.groups[group_id] = game_group( group_id, roomname, password, "", bootpass, persist = 1 ) | |
2790 cgmsg = "Create Temporary Persistant Group: (" + str(group_id) + ") " + str(roomname) | |
2791 self.log_msg( cgmsg ) | |
2792 self.send_to_all('0',self.groups[group_id].toxml('new')) | |
2793 self.send_to_all('0',self.groups[group_id].toxml('update')) | |
2794 return str("Persistant room created (group " + group_id + ").") | |
2795 | |
2796 #---------------------------------------------------------------- | |
2797 # Prune Room -- Added by Snowdog 6/03 | |
2798 # | |
2799 # similar to remove_room() except rooms are removed regardless | |
2800 # of them being persistant or not | |
2801 # | |
2802 # Added some error checking and updated room removal for per-room | |
2803 # based persistance -- Snowdog 4/04 | |
2804 #---------------------------------------------------------------- | |
2805 | |
2806 def prune_room(self,group): | |
2807 #don't allow lobby to be removed | |
2808 if group == '0': return "Lobby is required to exist and cannot be removed." | |
2809 | |
2810 #check that group id exists | |
2811 if group not in self.groups: | |
2812 return "Invalid Room Id. Ignoring remove request." | |
2813 | |
2814 try: | |
2815 keys = self.groups[group].get_player_ids() | |
2816 for k in keys: | |
2817 self.move_player(k,'0') | |
2818 | |
2819 ins = "Room" | |
2820 if self.isPersistentRoom(group) : ins="Persistant room" | |
2821 self.send_to_all("0",self.groups[group].toxml('del')) | |
2822 del self.groups[group] | |
2823 self.log_msg(("delete_group", ('0',group))) | |
2824 return ins + " removed." | |
2825 | |
2826 except: | |
2827 traceback.print_exc() | |
2828 return "An Error occured on the server during room removal!" | |
2829 | |
2830 | |
2831 #---------------------------------------------------------------- | |
2832 # Remote Player List -- Added by snowdog 6/03 | |
2833 # | |
2834 # Similar to console listing except formated for web display | |
2835 # in chat window on remote client | |
2836 #---------------------------------------------------------------- | |
2837 def player_list_remote(self): | |
2838 COLOR1 = "\"#004080\"" #header/footer background color | |
2839 COLOR2 = "\"#DDDDDD\"" #group line background color | |
2840 COLOR3 = "\"#FFFFFF\"" #player line background color | |
2841 COLOR4 = "\"#FFFFFF\"" #header/footer text color | |
2842 PCOLOR = "\"#000000\"" #Player text color | |
2843 LCOLOR = "\"#404040\"" #Lurker text color | |
2844 GCOLOR = "\"#FF0000\"" #GM text color | |
2845 SIZE = "size=\"-1\"" #player info text size | |
2846 FG = PCOLOR | |
2847 | |
2848 | |
2849 "display a condensed list of players on the server" | |
2850 self.p_lock.acquire() | |
2851 pl = "<br /><table border=\"0\" cellpadding=\"1\" cellspacing=\"2\">" | |
2852 pl += "<tr><td colspan='4' bgcolor=" + COLOR1 + "><font color=" + COLOR4 + "><b>GROUP & PLAYER LIST</b></font></td></tr>" | |
2853 try: | |
2854 | |
2855 keys = self.groups.keys() | |
2856 keys.sort(id_compare) | |
2857 for k in keys: | |
2858 groupstring = "<tr><td bgcolor=" + COLOR2 + " colspan='2'><b>Group " + str(k) + ": " + self.groups[k].name + "</b>" | |
2859 groupstring += "</td><td bgcolor=" + COLOR2 + " > <i>Password: \"" + self.groups[k].pwd + "\"</td><td bgcolor=" + COLOR2 + " > Boot: \"" + self.groups[k].boot_pwd + "\"</i></td></tr>" | |
2860 pl += groupstring | |
2861 ids = self.groups[k].get_player_ids() | |
2862 ids.sort(id_compare) | |
2863 for id in ids: | |
2864 if self.players.has_key(id): | |
2865 if k != "0": | |
2866 if (self.players[id]).role == "GM": FG = GCOLOR | |
2867 elif (self.players[id]).role == "Player": FG = PCOLOR | |
2868 else: FG = LCOLOR | |
2869 else: FG = PCOLOR | |
2870 pl += "<tr><td bgcolor=" + COLOR3 + ">" | |
2871 pl += "<font color=" + FG + " " + SIZE + "> (" + (self.players[id]).id + ") " | |
2872 pl += (self.players[id]).name | |
2873 pl += "</font></td><td bgcolor=" + COLOR3 + " ><font color=" + FG + " " + SIZE + ">[IP: " + (self.players[id]).ip + "]</font></td><td bgcolor=" + COLOR3 + " ><font color=" + FG + " " + SIZE + "> " | |
2874 pl += (self.players[id]).idle_status() | |
2875 pl += "</font></td><td><font color=" + FG + " " + SIZE + ">" | |
2876 pl += (self.players[id]).connected_time_string() | |
2877 pl += "</font>" | |
2878 | |
2879 else: | |
2880 self.groups[k].remove_player(id) | |
2881 pl +="<tr><td colspan='4' bgcolor=" + COLOR3 + " >Bad Player Ref (#" + id + ") in group" | |
2882 pl+="</td></tr>" | |
2883 pl += "<tr><td colspan='4' bgcolor=" + COLOR1 + "><font color=" + COLOR4 + "><b><i>Statistics: groups: " + str(len(self.groups)) + " players: " + str(len(self.players)) + "</i></b></font></td></tr></table>" | |
2884 except Exception, e: | |
2885 self.log_msg(str(e)) | |
2886 self.p_lock.release() | |
2887 return pl |